From 5da842297dfad0ce52724aa4fd7f19b1307b7a11 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Mon, 11 Oct 2021 18:09:46 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../javascripts/analytics/shared/constants.js | 1 + .../blob/file_template_mediator.js | 6 +- .../new/components/new_project_url_select.vue | 4 +- .../registry/explorer/constants/list.js | 2 +- .../javascripts/registry/explorer/index.js | 4 + .../registry/explorer/pages/list.vue | 5 +- .../registry/connection_errors_handler.rb | 38 ++++++++ .../registry/repositories_controller.rb | 1 + .../registry/repositories_controller.rb | 3 +- .../container_repository_details_type.rb | 6 ++ .../types/container_repository_type.rb | 6 ++ .../registry/repositories/index.html.haml | 3 +- .../registry/repositories/index.html.haml | 3 +- .../jira_connect_asymmetric_jwt.yml | 2 +- doc/api/repositories.md | 4 +- doc/development/snowplow/index.md | 8 -- doc/user/group/devops_adoption/index.md | 8 ++ lib/api/container_repositories.rb | 2 + lib/api/group_container_repositories.rb | 1 + lib/api/helpers.rb | 4 +- lib/api/helpers/container_registry_helpers.rb | 15 +++ lib/api/project_container_repositories.rb | 2 + lib/api/repositories.rb | 3 +- lib/container_registry/client.rb | 9 ++ locale/gitlab.pot | 13 ++- .../registry/repositories_controller_spec.rb | 25 +++++ .../registry/repositories_controller_spec.rb | 13 +++ .../groups/container_registry_spec.rb | 11 +++ .../projects/container_registry_spec.rb | 11 +++ .../security/project/internal_access_spec.rb | 1 + .../security/project/private_access_spec.rb | 1 + .../security/project/public_access_spec.rb | 1 + .../blob/file_template_mediator_spec.js | 53 +++++++++++ .../registry/explorer/pages/list_spec.js | 7 +- spec/lib/container_registry/client_spec.rb | 94 +++++++++++-------- .../api/container_repositories_spec.rb | 26 +++++ .../container_repository_details_spec.rb | 2 + .../group/container_repositories_spec.rb | 9 +- .../project/container_repositories_spec.rb | 9 +- .../api/group_container_repositories_spec.rb | 7 +- .../project_container_repositories_spec.rb | 5 + spec/requests/api/repositories_spec.rb | 12 +++ .../registry/repositories_controller_spec.rb | 1 + spec/support/helpers/stub_gitlab_calls.rb | 12 +++ .../container_registry_shared_examples.rb | 9 ++ .../container_repositories_shared_examples.rb | 37 ++++++++ 46 files changed, 431 insertions(+), 68 deletions(-) create mode 100644 app/controllers/concerns/registry/connection_errors_handler.rb create mode 100644 lib/api/helpers/container_registry_helpers.rb create mode 100644 spec/frontend/blob/file_template_mediator_spec.js create mode 100644 spec/support/shared_examples/features/container_registry_shared_examples.rb diff --git a/app/assets/javascripts/analytics/shared/constants.js b/app/assets/javascripts/analytics/shared/constants.js index 44d9b4b4262..c06bd34f86f 100644 --- a/app/assets/javascripts/analytics/shared/constants.js +++ b/app/assets/javascripts/analytics/shared/constants.js @@ -9,4 +9,5 @@ export const dateFormats = { isoDate, defaultDate: mediumDate, defaultDateTime: 'mmm d, yyyy h:MMtt', + month: 'mmmm', }; diff --git a/app/assets/javascripts/blob/file_template_mediator.js b/app/assets/javascripts/blob/file_template_mediator.js index 136457c115d..991f98c89e7 100644 --- a/app/assets/javascripts/blob/file_template_mediator.js +++ b/app/assets/javascripts/blob/file_template_mediator.js @@ -247,7 +247,11 @@ export default class FileTemplateMediator { } setFilename(name) { - this.$filenameInput.val(name).trigger('change'); + const input = this.$filenameInput.get(0); + if (name !== undefined && input.value !== name) { + input.value = name; + input.dispatchEvent(new Event('change')); + } } getSelected() { diff --git a/app/assets/javascripts/pages/projects/new/components/new_project_url_select.vue b/app/assets/javascripts/pages/projects/new/components/new_project_url_select.vue index e2572183082..bf44ff70562 100644 --- a/app/assets/javascripts/pages/projects/new/components/new_project_url_select.vue +++ b/app/assets/javascripts/pages/projects/new/components/new_project_url_select.vue @@ -107,7 +107,9 @@ export default { this.groupToFilterBy = this.userGroups.find( (group) => getIdFromGraphQLId(group.id) === groupId, ); - this.setNamespace(this.groupToFilterBy); + if (this.groupToFilterBy) { + this.setNamespace(this.groupToFilterBy); + } }, setNamespace({ id, fullPath }) { this.selectedNamespace = { diff --git a/app/assets/javascripts/registry/explorer/constants/list.js b/app/assets/javascripts/registry/explorer/constants/list.js index f59b9d7a9f5..d21a154d1b8 100644 --- a/app/assets/javascripts/registry/explorer/constants/list.js +++ b/app/assets/javascripts/registry/explorer/constants/list.js @@ -5,7 +5,7 @@ import { s__, __ } from '~/locale'; export const CONTAINER_REGISTRY_TITLE = s__('ContainerRegistry|Container Registry'); export const CONNECTION_ERROR_TITLE = s__('ContainerRegistry|Docker connection error'); export const CONNECTION_ERROR_MESSAGE = s__( - `ContainerRegistry|We are having trouble connecting to the Registry, which could be due to an issue with your project name or path. %{docLinkStart}More information%{docLinkEnd}`, + `ContainerRegistry|We are having trouble connecting to the Container Registry. Please try refreshing the page. If this error persists, please review %{docLinkStart}the troubleshooting documentation%{docLinkEnd}.`, ); export const LIST_INTRO_TEXT = s__( `ContainerRegistry|With the GitLab Container Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}`, diff --git a/app/assets/javascripts/registry/explorer/index.js b/app/assets/javascripts/registry/explorer/index.js index 1f82fd7f238..246a6768593 100644 --- a/app/assets/javascripts/registry/explorer/index.js +++ b/app/assets/javascripts/registry/explorer/index.js @@ -36,6 +36,8 @@ export default () => { isAdmin, showCleanupPolicyOnAlert, showUnfinishedTagCleanupCallout, + connectionError, + invalidPathError, ...config } = el.dataset; @@ -67,6 +69,8 @@ export default () => { isAdmin: parseBoolean(isAdmin), showCleanupPolicyOnAlert: parseBoolean(showCleanupPolicyOnAlert), showUnfinishedTagCleanupCallout: parseBoolean(showUnfinishedTagCleanupCallout), + connectionError: parseBoolean(connectionError), + invalidPathError: parseBoolean(invalidPathError), }, /* eslint-disable @gitlab/require-i18n-strings */ dockerBuildCommand: `docker build -t ${config.repositoryUrl} .`, diff --git a/app/assets/javascripts/registry/explorer/pages/list.vue b/app/assets/javascripts/registry/explorer/pages/list.vue index 3c8790fa6e5..73b957f42f2 100644 --- a/app/assets/javascripts/registry/explorer/pages/list.vue +++ b/app/assets/javascripts/registry/explorer/pages/list.vue @@ -171,6 +171,9 @@ export default { showDeleteAlert() { return this.deleteAlertType && this.itemToDelete?.path; }, + showConnectionError() { + return this.config.connectionError || this.config.invalidPathError; + }, deleteImageAlertMessage() { return this.deleteAlertType === 'success' ? DELETE_IMAGE_SUCCESS_MESSAGE @@ -292,7 +295,7 @@ export default { /> diff --git a/app/controllers/concerns/registry/connection_errors_handler.rb b/app/controllers/concerns/registry/connection_errors_handler.rb new file mode 100644 index 00000000000..2b24f3b5b31 --- /dev/null +++ b/app/controllers/concerns/registry/connection_errors_handler.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Registry + module ConnectionErrorsHandler + extend ActiveSupport::Concern + + included do + rescue_from ContainerRegistry::Path::InvalidRegistryPathError, with: :invalid_registry_path + rescue_from Faraday::Error, with: :connection_error + + before_action :ping_container_registry + end + + private + + # rubocop:disable Gitlab/ModuleWithInstanceVariables + # These instance variables are only read by a view helper to pass + # them to the frontend + # See app/views/projects/registry/repositories/index.html.haml + # app/views/groups/registry/repositories/index.html.haml + def invalid_registry_path + @invalid_path_error = true + + render :index + end + + def connection_error + @connection_error = true + + render :index + end + # rubocop:enable Gitlab/ModuleWithInstanceVariables + + def ping_container_registry + ContainerRegistry::Client.registry_info + end + end +end diff --git a/app/controllers/groups/registry/repositories_controller.rb b/app/controllers/groups/registry/repositories_controller.rb index 3aaaf6ade6b..549a148bfb8 100644 --- a/app/controllers/groups/registry/repositories_controller.rb +++ b/app/controllers/groups/registry/repositories_controller.rb @@ -3,6 +3,7 @@ module Groups module Registry class RepositoriesController < Groups::ApplicationController include PackagesHelper + include ::Registry::ConnectionErrorsHandler before_action :verify_container_registry_enabled! before_action :authorize_read_container_image! diff --git a/app/controllers/projects/registry/repositories_controller.rb b/app/controllers/projects/registry/repositories_controller.rb index 8acebd89033..ad3b2bc98e7 100644 --- a/app/controllers/projects/registry/repositories_controller.rb +++ b/app/controllers/projects/registry/repositories_controller.rb @@ -4,6 +4,7 @@ module Projects module Registry class RepositoriesController < ::Projects::Registry::ApplicationController include PackagesHelper + include ::Registry::ConnectionErrorsHandler before_action :authorize_update_container_image!, only: [:destroy] @@ -48,8 +49,6 @@ module Projects repository.save! if repository.has_tags? end end - rescue ContainerRegistry::Path::InvalidRegistryPathError - @character_error = true end end end diff --git a/app/graphql/types/container_repository_details_type.rb b/app/graphql/types/container_repository_details_type.rb index 1a9f57e701f..8190cc9bc25 100644 --- a/app/graphql/types/container_repository_details_type.rb +++ b/app/graphql/types/container_repository_details_type.rb @@ -17,5 +17,11 @@ module Types def can_delete Ability.allowed?(current_user, :destroy_container_image, object) end + + def tags + object.tags + rescue Faraday::Error + raise ::Gitlab::Graphql::Errors::ResourceNotAvailable, 'We are having trouble connecting to the Container Registry. If this error persists, please review the troubleshooting documentation.' + end end end diff --git a/app/graphql/types/container_repository_type.rb b/app/graphql/types/container_repository_type.rb index 67093f57862..1fe5cf112f0 100644 --- a/app/graphql/types/container_repository_type.rb +++ b/app/graphql/types/container_repository_type.rb @@ -28,5 +28,11 @@ module Types def project Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.project_id).find end + + def tags_count + object.tags_count + rescue Faraday::Error + raise ::Gitlab::Graphql::Errors::ResourceNotAvailable, 'We are having trouble connecting to the Container Registry. If this error persists, please review the troubleshooting documentation.' + end end end diff --git a/app/views/groups/registry/repositories/index.html.haml b/app/views/groups/registry/repositories/index.html.haml index fa6bd021e45..2901c8fa46b 100644 --- a/app/views/groups/registry/repositories/index.html.haml +++ b/app/views/groups/registry/repositories/index.html.haml @@ -16,7 +16,8 @@ is_group_page: "true", "group_path": @group.full_path, "gid_prefix": container_repository_gid_prefix, - character_error: @character_error.to_s, + connection_error: (!!@connection_error).to_s, + invalid_path_error: (!!@invalid_path_error).to_s, user_callouts_path: user_callouts_path, user_callout_id: UserCalloutsHelper::UNFINISHED_TAG_CLEANUP_CALLOUT, show_unfinished_tag_cleanup_callout: show_unfinished_tag_cleanup_callout?.to_s } } diff --git a/app/views/projects/registry/repositories/index.html.haml b/app/views/projects/registry/repositories/index.html.haml index bdb5f021b70..cfdbf3410b1 100644 --- a/app/views/projects/registry/repositories/index.html.haml +++ b/app/views/projects/registry/repositories/index.html.haml @@ -20,7 +20,8 @@ "is_admin": current_user&.admin.to_s, "show_cleanup_policy_on_alert": show_cleanup_policy_on_alert(@project).to_s, "cleanup_policies_settings_path": project_settings_packages_and_registries_path(@project), - character_error: @character_error.to_s, + connection_error: (!!@connection_error).to_s, + invalid_path_error: (!!@invalid_path_error).to_s, user_callouts_path: user_callouts_path, user_callout_id: UserCalloutsHelper::UNFINISHED_TAG_CLEANUP_CALLOUT, show_unfinished_tag_cleanup_callout: show_unfinished_tag_cleanup_callout?.to_s, } } diff --git a/config/feature_flags/development/jira_connect_asymmetric_jwt.yml b/config/feature_flags/development/jira_connect_asymmetric_jwt.yml index 6f8d84962b2..e204a7d6fac 100644 --- a/config/feature_flags/development/jira_connect_asymmetric_jwt.yml +++ b/config/feature_flags/development/jira_connect_asymmetric_jwt.yml @@ -1,7 +1,7 @@ --- name: jira_connect_asymmetric_jwt introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71080 -rollout_issue_url: +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/342808 milestone: '14.4' type: development group: group::integrations diff --git a/doc/api/repositories.md b/doc/api/repositories.md index f1651ef04af..e93ffbc5e72 100644 --- a/doc/api/repositories.md +++ b/doc/api/repositories.md @@ -126,6 +126,7 @@ Supported attributes: ## Get file archive > Support for [including Git LFS blobs](../topics/git/lfs/index.md#lfs-objects-in-project-archives) was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15079) in GitLab 13.5. +> Support for downloading a subfolder was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/28827) in GitLab 14.4. Get an archive of the repository. This endpoint can be accessed without authentication if the repository is publicly accessible. @@ -147,11 +148,12 @@ Supported attributes: |:------------|:---------------|:---------|:----------------------| | `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. | | `sha` | string | no | The commit SHA to download. A tag, branch reference, or SHA can be used. This defaults to the tip of the default branch if not specified. | +| `path` | string | no | The subpath of the repository to download. This defaults to the whole repository (empty string). | Example request: ```shell -curl --header "PRIVATE-TOKEN: " "https://gitlab.com/api/v4/projects//repository/archive?sha=" +curl --header "PRIVATE-TOKEN: " "https://gitlab.com/api/v4/projects//repository/archive?sha=&path=" ``` ## Compare branches, tags or commits diff --git a/doc/development/snowplow/index.md b/doc/development/snowplow/index.md index 313a62ec6c8..4bebd24d6d5 100644 --- a/doc/development/snowplow/index.md +++ b/doc/development/snowplow/index.md @@ -156,11 +156,3 @@ LIMIT 20 ### Web-specific parameters Snowplow JS adds [web-specific parameters](https://docs.snowplowanalytics.com/docs/collecting-data/collecting-from-own-applications/snowplow-tracker-protocol/#Web-specific_parameters) to all web events by default. - -## Implement Snowplow tracking - -See the [Implementation](implementation.md) guide. - -## Snowplow schemas - -See the [Snowplow schemas](schemas.md) guide. diff --git a/doc/user/group/devops_adoption/index.md b/doc/user/group/devops_adoption/index.md index 554d01039ad..7f936e080f0 100644 --- a/doc/user/group/devops_adoption/index.md +++ b/doc/user/group/devops_adoption/index.md @@ -14,6 +14,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w > - Dependency Scanning metrics [added](https://gitlab.com/gitlab-org/gitlab/-/issues/328034) in GitLab 14.2. > - Multiselect [added](https://gitlab.com/gitlab-org/gitlab/-/issues/333586) in GitLab 14.2. > - Overview table [added](https://gitlab.com/gitlab-org/gitlab/-/issues/335638) in GitLab 14.3. +> - Adoption over time chart [added](https://gitlab.com/gitlab-org/gitlab/-/issues/337561) in GitLab 14.4. Prerequisites: @@ -69,6 +70,13 @@ Each group appears as a separate row in the table. For each row, a feature is considered "adopted" if it has been used in a project in the given group during the time period (including projects in any subgroups of the given group). +## Adoption over time + +The **Adoption over time** chart in the **Overview** tab displays DevOps Adoption over time. The chart displays the total number of adopted features from the previous twelve months, +from when you enabled DevOps Adoption for the group. + +The tooltip displays information about the features tracked for individual months. + ## When is a feature considered adopted A feature is considered "adopted" if it has been used anywhere in the group in the specified time. diff --git a/lib/api/container_repositories.rb b/lib/api/container_repositories.rb index c84527f26e7..9cd3e449687 100644 --- a/lib/api/container_repositories.rb +++ b/lib/api/container_repositories.rb @@ -3,6 +3,8 @@ module API class ContainerRepositories < ::API::Base include Gitlab::Utils::StrongMemoize + include ::API::Helpers::ContainerRegistryHelpers + helpers ::API::Helpers::PackagesHelpers before { authenticate! } diff --git a/lib/api/group_container_repositories.rb b/lib/api/group_container_repositories.rb index 96175f31696..55e18fd1370 100644 --- a/lib/api/group_container_repositories.rb +++ b/lib/api/group_container_repositories.rb @@ -3,6 +3,7 @@ module API class GroupContainerRepositories < ::API::Base include PaginationParams + include ::API::Helpers::ContainerRegistryHelpers helpers ::API::Helpers::PackagesHelpers diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index ff3590d6c13..f9ba5ba8186 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -430,8 +430,8 @@ module API render_api_error!('406 Not Acceptable', 406) end - def service_unavailable! - render_api_error!('503 Service Unavailable', 503) + def service_unavailable!(message = nil) + render_api_error!(message || '503 Service Unavailable', 503) end def conflict!(message = nil) diff --git a/lib/api/helpers/container_registry_helpers.rb b/lib/api/helpers/container_registry_helpers.rb new file mode 100644 index 00000000000..9c844e364eb --- /dev/null +++ b/lib/api/helpers/container_registry_helpers.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module API + module Helpers + module ContainerRegistryHelpers + extend ActiveSupport::Concern + + included do + rescue_from Faraday::Error, ContainerRegistry::Path::InvalidRegistryPathError do |e| + service_unavailable!('We are having trouble connecting to the Container Registry. If this error persists, please review the troubleshooting documentation.') + end + end + end + end +end diff --git a/lib/api/project_container_repositories.rb b/lib/api/project_container_repositories.rb index 28cfa9e3ae0..82b6082c3fe 100644 --- a/lib/api/project_container_repositories.rb +++ b/lib/api/project_container_repositories.rb @@ -3,6 +3,8 @@ module API class ProjectContainerRepositories < ::API::Base include PaginationParams + include ::API::Helpers::ContainerRegistryHelpers + helpers ::API::Helpers::PackagesHelpers REPOSITORY_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge( diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb index 3c9255e3117..1aa76906b3d 100644 --- a/lib/api/repositories.rb +++ b/lib/api/repositories.rb @@ -101,6 +101,7 @@ module API params do optional :sha, type: String, desc: 'The commit sha of the archive to be downloaded' optional :format, type: String, desc: 'The archive format' + optional :path, type: String, desc: 'Subfolder of the repository to be downloaded' end get ':id/repository/archive', requirements: { format: Gitlab::PathRegex.archive_formats_regex } do if archive_rate_limit_reached?(current_user, user_project) @@ -109,7 +110,7 @@ module API not_acceptable! if Gitlab::HotlinkingDetector.intercept_hotlinking?(request) - send_git_archive user_project.repository, ref: params[:sha], format: params[:format], append_sha: true + send_git_archive user_project.repository, ref: params[:sha], format: params[:format], append_sha: true, path: params[:path] rescue StandardError not_found!('File') end diff --git a/lib/container_registry/client.rb b/lib/container_registry/client.rb index cc692140301..46399224a5d 100644 --- a/lib/container_registry/client.rb +++ b/lib/container_registry/client.rb @@ -51,6 +51,15 @@ module ContainerRegistry client.supports_tag_delete? end + def self.registry_info + registry_config = Gitlab.config.registry + return unless registry_config.enabled && registry_config.api_url.present? + + token = Auth::ContainerRegistryAuthenticationService.access_token([], []) + client = new(registry_config.api_url, token: token) + client.registry_info + end + def initialize(base_uri, options = {}) @base_uri = base_uri @options = options diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 4b27cc3b836..7dd64106aeb 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -9111,7 +9111,7 @@ msgstr "" msgid "ContainerRegistry|To widen your search, change or remove the filters above." msgstr "" -msgid "ContainerRegistry|We are having trouble connecting to the Registry, which could be due to an issue with your project name or path. %{docLinkStart}More information%{docLinkEnd}" +msgid "ContainerRegistry|We are having trouble connecting to the Container Registry. Please try refreshing the page. If this error persists, please review %{docLinkStart}the troubleshooting documentation%{docLinkEnd}." msgstr "" msgid "ContainerRegistry|With the Container Registry, every project can have its own space to store its Docker images. %{docLinkStart}More Information%{docLinkEnd}" @@ -11704,6 +11704,9 @@ msgstr "" msgid "DevopsAdoption|Adoption by subgroup" msgstr "" +msgid "DevopsAdoption|Adoption over time" +msgstr "" + msgid "DevopsAdoption|An error occurred while removing the group. Please try again." msgstr "" @@ -11782,6 +11785,9 @@ msgstr "" msgid "DevopsAdoption|No results…" msgstr "" +msgid "DevopsAdoption|No tracked features" +msgstr "" + msgid "DevopsAdoption|Not adopted" msgstr "" @@ -11827,6 +11833,9 @@ msgstr "" msgid "DevopsAdoption|This group has no subgroups" msgstr "" +msgid "DevopsAdoption|Total number of features adopted" +msgstr "" + msgid "DevopsAdoption|You cannot remove the group you are currently in." msgstr "" @@ -24104,7 +24113,7 @@ msgstr "" msgid "PQL|Submit information" msgstr "" -msgid "PQL|Thank you for reaching out! Our sales team will bet back to you soon." +msgid "PQL|Thank you for reaching out! Our sales team will get back to you soon." msgstr "" msgid "Package Registry" diff --git a/spec/controllers/groups/registry/repositories_controller_spec.rb b/spec/controllers/groups/registry/repositories_controller_spec.rb index f4541eda293..9ac19b06718 100644 --- a/spec/controllers/groups/registry/repositories_controller_spec.rb +++ b/spec/controllers/groups/registry/repositories_controller_spec.rb @@ -19,6 +19,7 @@ RSpec.describe Groups::Registry::RepositoriesController do before do stub_container_registry_config(enabled: true) stub_container_registry_tags(repository: :any, tags: []) + stub_container_registry_info group.add_owner(user) group.add_guest(guest) sign_in(user) @@ -37,6 +38,18 @@ RSpec.describe Groups::Registry::RepositoriesController do 'name' => repo.name ) end + + [ContainerRegistry::Path::InvalidRegistryPathError, Faraday::Error].each do |error_class| + context "when there is a #{error_class}" do + it 'displays a connection error message' do + expect(::ContainerRegistry::Client).to receive(:registry_info).and_raise(error_class, nil, nil) + + subject + + expect(response).to have_gitlab_http_status(:ok) + end + end + end end shared_examples 'with name parameter' do @@ -71,6 +84,18 @@ RSpec.describe Groups::Registry::RepositoriesController do expect(response).to have_gitlab_http_status(:ok) expect_no_snowplow_event end + + [ContainerRegistry::Path::InvalidRegistryPathError, Faraday::Error].each do |error_class| + context "when there is an invalid path error #{error_class}" do + it 'displays a connection error message' do + expect(::ContainerRegistry::Client).to receive(:registry_info).and_raise(error_class, nil, nil) + + subject + + expect(response).to have_gitlab_http_status(:ok) + end + end + end end context 'json format' do diff --git a/spec/controllers/projects/registry/repositories_controller_spec.rb b/spec/controllers/projects/registry/repositories_controller_spec.rb index 0685e5a2055..a5faaaf5969 100644 --- a/spec/controllers/projects/registry/repositories_controller_spec.rb +++ b/spec/controllers/projects/registry/repositories_controller_spec.rb @@ -9,6 +9,7 @@ RSpec.describe Projects::Registry::RepositoriesController do before do sign_in(user) stub_container_registry_config(enabled: true) + stub_container_registry_info end context 'when user has access to registry' do @@ -30,6 +31,18 @@ RSpec.describe Projects::Registry::RepositoriesController do expect(response).to have_gitlab_http_status(:not_found) end + + [ContainerRegistry::Path::InvalidRegistryPathError, Faraday::Error].each do |error_class| + context "when there is a #{error_class}" do + it 'displays a connection error message' do + expect(::ContainerRegistry::Client).to receive(:registry_info).and_raise(error_class, nil, nil) + + go_to_index + + expect(response).to have_gitlab_http_status(:ok) + end + end + end end shared_examples 'renders a list of repositories' do diff --git a/spec/features/groups/container_registry_spec.rb b/spec/features/groups/container_registry_spec.rb index be694374d62..098559dc3f8 100644 --- a/spec/features/groups/container_registry_spec.rb +++ b/spec/features/groups/container_registry_spec.rb @@ -16,6 +16,7 @@ RSpec.describe 'Container Registry', :js do sign_in(user) stub_container_registry_config(enabled: true) stub_container_registry_tags(repository: :any, tags: []) + stub_container_registry_info end it 'has a page title set' do @@ -57,6 +58,16 @@ RSpec.describe 'Container Registry', :js do expect(page).to have_content 'latest' end + [ContainerRegistry::Path::InvalidRegistryPathError, Faraday::Error].each do |error_class| + context "when there is a #{error_class}" do + before do + expect(::ContainerRegistry::Client).to receive(:registry_info).and_raise(error_class, nil, nil) + end + + it_behaves_like 'handling feature network errors with the container registry' + end + end + describe 'image repo details' do before do visit_container_registry_details 'my/image' diff --git a/spec/features/projects/container_registry_spec.rb b/spec/features/projects/container_registry_spec.rb index facee24f656..eec50c3a66a 100644 --- a/spec/features/projects/container_registry_spec.rb +++ b/spec/features/projects/container_registry_spec.rb @@ -20,6 +20,7 @@ RSpec.describe 'Container Registry', :js do sign_in(user) project.add_developer(user) stub_container_registry_config(enabled: true) + stub_container_registry_info stub_container_registry_tags(repository: :any, tags: []) end @@ -122,6 +123,16 @@ RSpec.describe 'Container Registry', :js do expect(page).to have_content('Digest: N/A') end end + + [ContainerRegistry::Path::InvalidRegistryPathError, Faraday::Error].each do |error_class| + context "when there is a #{error_class}" do + before do + expect(::ContainerRegistry::Client).to receive(:registry_info).and_raise(error_class, nil, nil) + end + + it_behaves_like 'handling feature network errors with the container registry' + end + end end describe 'image repo details when image has no name' do diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb index 9dcef13757a..4012a302196 100644 --- a/spec/features/security/project/internal_access_spec.rb +++ b/spec/features/security/project/internal_access_spec.rb @@ -553,6 +553,7 @@ RSpec.describe "Internal Project Access" do before do stub_container_registry_tags(repository: :any, tags: ['latest']) stub_container_registry_config(enabled: true) + stub_container_registry_info project.container_repositories << container_repository end diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb index 5a200bea80a..aa34ccce2c1 100644 --- a/spec/features/security/project/private_access_spec.rb +++ b/spec/features/security/project/private_access_spec.rb @@ -570,6 +570,7 @@ RSpec.describe "Private Project Access" do before do stub_container_registry_tags(repository: :any, tags: ['latest']) stub_container_registry_config(enabled: true) + stub_container_registry_info project.container_repositories << container_repository end diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb index 8ceb6920e77..abe128c6f78 100644 --- a/spec/features/security/project/public_access_spec.rb +++ b/spec/features/security/project/public_access_spec.rb @@ -552,6 +552,7 @@ RSpec.describe "Public Project Access" do before do stub_container_registry_tags(repository: :any, tags: ['latest']) stub_container_registry_config(enabled: true) + stub_container_registry_info project.container_repositories << container_repository end diff --git a/spec/frontend/blob/file_template_mediator_spec.js b/spec/frontend/blob/file_template_mediator_spec.js new file mode 100644 index 00000000000..44e12deb564 --- /dev/null +++ b/spec/frontend/blob/file_template_mediator_spec.js @@ -0,0 +1,53 @@ +import TemplateSelectorMediator from '~/blob/file_template_mediator'; + +describe('Template Selector Mediator', () => { + let mediator; + + describe('setFilename', () => { + let input; + const newFileName = 'foo'; + const editor = jest.fn().mockImplementationOnce(() => ({ + getValue: jest.fn().mockImplementation(() => {}), + }))(); + + beforeEach(() => { + setFixtures('
'); + input = document.querySelector('.js-file-path-name-input'); + mediator = new TemplateSelectorMediator({ + editor, + currentAction: jest.fn(), + projectId: jest.fn(), + }); + }); + + it('fills out the input field', () => { + expect(input.value).toBe(''); + mediator.setFilename(newFileName); + expect(input.value).toBe(newFileName); + }); + + it.each` + name | newName | shouldDispatch + ${newFileName} | ${newFileName} | ${false} + ${newFileName} | ${''} | ${true} + ${newFileName} | ${undefined} | ${false} + ${''} | ${''} | ${false} + ${''} | ${newFileName} | ${true} + ${''} | ${undefined} | ${false} + `( + 'correctly reacts to the name change when current name is $name and newName is $newName', + ({ name, newName, shouldDispatch }) => { + input.value = name; + const eventHandler = jest.fn(); + input.addEventListener('change', eventHandler); + + mediator.setFilename(newName); + if (shouldDispatch) { + expect(eventHandler).toHaveBeenCalledTimes(1); + } else { + expect(eventHandler).not.toHaveBeenCalled(); + } + }, + ); + }); +}); diff --git a/spec/frontend/registry/explorer/pages/list_spec.js b/spec/frontend/registry/explorer/pages/list_spec.js index b58a53f0af2..e1f24a2b65b 100644 --- a/spec/frontend/registry/explorer/pages/list_spec.js +++ b/spec/frontend/registry/explorer/pages/list_spec.js @@ -129,13 +129,16 @@ describe('List Page', () => { }); }); - describe('connection error', () => { + describe.each([ + { error: 'connectionError', errorName: 'connection error' }, + { error: 'invalidPathError', errorName: 'invalid path error' }, + ])('handling $errorName', ({ error }) => { const config = { - characterError: true, containersErrorImage: 'foo', helpPagePath: 'bar', isGroupPage: false, }; + config[error] = true; it('should show an empty state', () => { mountComponent({ config }); diff --git a/spec/lib/container_registry/client_spec.rb b/spec/lib/container_registry/client_spec.rb index 9d6f4db537d..47a8fcf5dd0 100644 --- a/spec/lib/container_registry/client_spec.rb +++ b/spec/lib/container_registry/client_spec.rb @@ -111,6 +111,49 @@ RSpec.describe ContainerRegistry::Client do it_behaves_like 'handling timeouts' end + shared_examples 'handling repository info' do + context 'when the check is successful' do + context 'when using the GitLab container registry' do + before do + stub_registry_info(headers: { + 'GitLab-Container-Registry-Version' => '2.9.1-gitlab', + 'GitLab-Container-Registry-Features' => 'a,b,c' + }) + end + + it 'identifies the vendor as "gitlab"' do + expect(subject).to include(vendor: 'gitlab') + end + + it 'identifies version and features' do + expect(subject).to include(version: '2.9.1-gitlab', features: %w[a b c]) + end + end + + context 'when using a third-party container registry' do + before do + stub_registry_info + end + + it 'identifies the vendor as "other"' do + expect(subject).to include(vendor: 'other') + end + + it 'does not identify version or features' do + expect(subject).to include(version: nil, features: []) + end + end + end + + context 'when the check is not successful' do + it 'does not identify vendor, version or features' do + stub_registry_info(status: 500) + + expect(subject).to eq({}) + end + end + end + describe '#repository_manifest' do subject { client.repository_manifest('group/test', 'mytag') } @@ -316,46 +359,7 @@ RSpec.describe ContainerRegistry::Client do describe '#registry_info' do subject { client.registry_info } - context 'when the check is successful' do - context 'when using the GitLab container registry' do - before do - stub_registry_info(headers: { - 'GitLab-Container-Registry-Version' => '2.9.1-gitlab', - 'GitLab-Container-Registry-Features' => 'a,b,c' - }) - end - - it 'identifies the vendor as "gitlab"' do - expect(subject).to include(vendor: 'gitlab') - end - - it 'identifies version and features' do - expect(subject).to include(version: '2.9.1-gitlab', features: %w[a b c]) - end - end - - context 'when using a third-party container registry' do - before do - stub_registry_info - end - - it 'identifies the vendor as "other"' do - expect(subject).to include(vendor: 'other') - end - - it 'does not identify version or features' do - expect(subject).to include(version: nil, features: []) - end - end - end - - context 'when the check is not successful' do - it 'does not identify vendor, version or features' do - stub_registry_info(status: 500) - - expect(subject).to eq({}) - end - end + it_behaves_like 'handling repository info' end describe '.supports_tag_delete?' do @@ -418,6 +422,16 @@ RSpec.describe ContainerRegistry::Client do end end + describe '.registry_info' do + subject { described_class.registry_info } + + before do + stub_container_registry_config(enabled: true, api_url: registry_api_url, key: 'spec/fixtures/x509_certificate_pk.key') + end + + it_behaves_like 'handling repository info' + end + def stub_upload(path, content, digest, status = 200) stub_request(:post, "#{registry_api_url}/v2/#{path}/blobs/uploads/") .with(headers: headers_with_accept_types) diff --git a/spec/requests/api/container_repositories_spec.rb b/spec/requests/api/container_repositories_spec.rb index 8d7494ffce1..9809702467d 100644 --- a/spec/requests/api/container_repositories_spec.rb +++ b/spec/requests/api/container_repositories_spec.rb @@ -48,6 +48,19 @@ RSpec.describe API::ContainerRepositories do expect(response).to match_response_schema('registry/repository') end + context 'with a network error' do + before do + stub_container_registry_network_error(client_method: :repository_tags) + end + + it 'returns a matching schema' do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('registry/repository') + end + end + context 'with tags param' do let(:url) { "/registry/repositories/#{repository.id}?tags=true" } @@ -61,6 +74,19 @@ RSpec.describe API::ContainerRepositories do expect(json_response['id']).to eq(repository.id) expect(response.body).to include('tags') end + + context 'with a network error' do + before do + stub_container_registry_network_error(client_method: :repository_tags) + end + + it 'returns a connection error message' do + subject + + expect(response).to have_gitlab_http_status(:service_unavailable) + expect(json_response['message']).to include('We are having trouble connecting to the Container Registry') + end + end end context 'with tags_count param' do diff --git a/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb b/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb index 356e1e11def..d93afcc0f33 100644 --- a/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb +++ b/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb @@ -153,4 +153,6 @@ RSpec.describe 'container repository details' do end end end + + it_behaves_like 'handling graphql network errors with the container registry' end diff --git a/spec/requests/api/graphql/group/container_repositories_spec.rb b/spec/requests/api/graphql/group/container_repositories_spec.rb index 939d7791d92..be0b866af4a 100644 --- a/spec/requests/api/graphql/group/container_repositories_spec.rb +++ b/spec/requests/api/graphql/group/container_repositories_spec.rb @@ -14,11 +14,12 @@ RSpec.describe 'getting container repositories in a group' do let_it_be(:container_repositories) { [container_repository, container_repositories_delete_scheduled, container_repositories_delete_failed].flatten } let_it_be(:container_expiration_policy) { project.container_expiration_policy } + let(:excluded_fields) { [] } let(:container_repositories_fields) do <<~GQL edges { node { - #{all_graphql_fields_for('container_repositories'.classify, max_depth: 1)} + #{all_graphql_fields_for('container_repositories'.classify, max_depth: 1, excluded: excluded_fields)} } } GQL @@ -152,6 +153,12 @@ RSpec.describe 'getting container repositories in a group' do end end + it_behaves_like 'handling graphql network errors with the container registry' + + it_behaves_like 'not hitting graphql network errors with the container registry' do + let(:excluded_fields) { %w[tags tagsCount] } + end + it 'returns the total count of container repositories' do subject diff --git a/spec/requests/api/graphql/project/container_repositories_spec.rb b/spec/requests/api/graphql/project/container_repositories_spec.rb index 9061e5ff40c..692143b2215 100644 --- a/spec/requests/api/graphql/project/container_repositories_spec.rb +++ b/spec/requests/api/graphql/project/container_repositories_spec.rb @@ -12,11 +12,12 @@ RSpec.describe 'getting container repositories in a project' do let_it_be(:container_repositories) { [container_repository, container_repositories_delete_scheduled, container_repositories_delete_failed].flatten } let_it_be(:container_expiration_policy) { project.container_expiration_policy } + let(:excluded_fields) { %w[pipeline jobs] } let(:container_repositories_fields) do <<~GQL edges { node { - #{all_graphql_fields_for('container_repositories'.classify, excluded: %w(pipeline jobs))} + #{all_graphql_fields_for('container_repositories'.classify, excluded: excluded_fields)} } } GQL @@ -151,6 +152,12 @@ RSpec.describe 'getting container repositories in a project' do end end + it_behaves_like 'handling graphql network errors with the container registry' + + it_behaves_like 'not hitting graphql network errors with the container registry' do + let(:excluded_fields) { %w[pipeline jobs tags tagsCount] } + end + it 'returns the total count of container repositories' do subject diff --git a/spec/requests/api/group_container_repositories_spec.rb b/spec/requests/api/group_container_repositories_spec.rb index fdbf910e4bc..bf29bd91414 100644 --- a/spec/requests/api/group_container_repositories_spec.rb +++ b/spec/requests/api/group_container_repositories_spec.rb @@ -20,12 +20,14 @@ RSpec.describe API::GroupContainerRepositories do end let(:api_user) { reporter } + let(:params) { {} } before do group.add_reporter(reporter) group.add_guest(guest) stub_container_registry_config(enabled: true) + stub_container_registry_info root_repository test_repository @@ -35,10 +37,13 @@ RSpec.describe API::GroupContainerRepositories do let(:url) { "/groups/#{group.id}/registry/repositories" } let(:snowplow_gitlab_standard_context) { { user: api_user, namespace: group } } - subject { get api(url, api_user) } + subject { get api(url, api_user), params: params } it_behaves_like 'rejected container repository access', :guest, :forbidden it_behaves_like 'rejected container repository access', :anonymous, :not_found + it_behaves_like 'handling network errors with the container registry' do + let(:params) { { tags: true } } + end it_behaves_like 'returns repositories for allowed users', :reporter, 'group' do let(:object) { group } diff --git a/spec/requests/api/project_container_repositories_spec.rb b/spec/requests/api/project_container_repositories_spec.rb index 1170a9ba6cb..196b0395ec0 100644 --- a/spec/requests/api/project_container_repositories_spec.rb +++ b/spec/requests/api/project_container_repositories_spec.rb @@ -52,6 +52,7 @@ RSpec.describe API::ProjectContainerRepositories do test_repository stub_container_registry_config(enabled: true) + stub_container_registry_info end shared_context 'using API user' do @@ -105,6 +106,9 @@ RSpec.describe API::ProjectContainerRepositories do it_behaves_like 'rejected container repository access', :guest, :forbidden unless context == 'using job token' it_behaves_like 'rejected container repository access', :anonymous, :not_found it_behaves_like 'a package tracking event', described_class.name, 'list_repositories' + it_behaves_like 'handling network errors with the container registry' do + let(:params) { { tags: true } } + end it_behaves_like 'returns repositories for allowed users', :reporter, 'project' do let(:object) { project } @@ -154,6 +158,7 @@ RSpec.describe API::ProjectContainerRepositories do it_behaves_like 'rejected container repository access', :guest, :forbidden unless context == 'using job token' it_behaves_like 'rejected container repository access', :anonymous, :not_found + it_behaves_like 'handling network errors with the container registry' context 'for reporter' do let(:api_user) { reporter } diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index a576e1ab1ee..f05f125c974 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -305,6 +305,18 @@ RSpec.describe API::Repositories do end end + it 'returns only a part of the repository with path set' do + path = 'bar' + get api("#{route}?path=#{path}", current_user) + + expect(response).to have_gitlab_http_status(:ok) + + type, params = workhorse_send_data + + expect(type).to eq('git-archive') + expect(params['ArchivePath']).to match(/#{project.path}\-[^\.]+\-#{path}\.tar.gz/) + end + it 'rate limits user when thresholds hit' do allow(::Gitlab::ApplicationRateLimiter).to receive(:throttled?).and_return(true) diff --git a/spec/requests/groups/registry/repositories_controller_spec.rb b/spec/requests/groups/registry/repositories_controller_spec.rb index 89cbd3e4100..0699f48c2be 100644 --- a/spec/requests/groups/registry/repositories_controller_spec.rb +++ b/spec/requests/groups/registry/repositories_controller_spec.rb @@ -9,6 +9,7 @@ RSpec.describe Groups::Registry::RepositoriesController do before do stub_container_registry_config(enabled: true) stub_container_registry_tags(repository: :any, tags: []) + stub_container_registry_info group.add_reporter(user) login_as(user) end diff --git a/spec/support/helpers/stub_gitlab_calls.rb b/spec/support/helpers/stub_gitlab_calls.rb index 5ab778c11cb..6f530d57caf 100644 --- a/spec/support/helpers/stub_gitlab_calls.rb +++ b/spec/support/helpers/stub_gitlab_calls.rb @@ -79,6 +79,18 @@ module StubGitlabCalls end end + def stub_container_registry_info(info: {}) + allow(ContainerRegistry::Client) + .to receive(:registry_info) + .and_return(info) + end + + def stub_container_registry_network_error(client_method:) + allow_next_instance_of(ContainerRegistry::Client) do |client| + allow(client).to receive(client_method).and_raise(::Faraday::Error, nil, nil) + end + end + def stub_commonmark_sourcepos_disabled allow_any_instance_of(Banzai::Filter::MarkdownEngines::CommonMark) .to receive(:render_options) diff --git a/spec/support/shared_examples/features/container_registry_shared_examples.rb b/spec/support/shared_examples/features/container_registry_shared_examples.rb new file mode 100644 index 00000000000..06b2b8c621c --- /dev/null +++ b/spec/support/shared_examples/features/container_registry_shared_examples.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'handling feature network errors with the container registry' do + it 'displays the error message' do + visit_container_registry + + expect(page).to have_content 'We are having trouble connecting to the Container Registry' + end +end diff --git a/spec/support/shared_examples/requests/api/container_repositories_shared_examples.rb b/spec/support/shared_examples/requests/api/container_repositories_shared_examples.rb index e776cf13217..e1e75be2494 100644 --- a/spec/support/shared_examples/requests/api/container_repositories_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/container_repositories_shared_examples.rb @@ -79,3 +79,40 @@ RSpec.shared_examples 'returns repositories for allowed users' do |user_type, sc end end end + +RSpec.shared_examples 'handling network errors with the container registry' do + before do + stub_container_registry_network_error(client_method: :repository_tags) + end + + it 'returns a connection error' do + subject + + expect(response).to have_gitlab_http_status(:service_unavailable) + expect(json_response['message']).to include('We are having trouble connecting to the Container Registry') + end +end + +RSpec.shared_examples 'handling graphql network errors with the container registry' do + before do + stub_container_registry_network_error(client_method: :repository_tags) + end + + it 'returns a connection error' do + subject + + expect_graphql_errors_to_include('We are having trouble connecting to the Container Registry') + end +end + +RSpec.shared_examples 'not hitting graphql network errors with the container registry' do + before do + stub_container_registry_network_error(client_method: :repository_tags) + end + + it 'does not return any error' do + subject + + expect_graphql_errors_to_be_empty + end +end