diff --git a/app/controllers/groups/harbor/repositories_controller.rb b/app/controllers/groups/harbor/repositories_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..364607f9b208541f1d9b38cb1ccdef884d6abd89 --- /dev/null +++ b/app/controllers/groups/harbor/repositories_controller.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Groups + module Harbor + class RepositoriesController < Groups::ApplicationController + feature_category :integrations + + before_action :harbor_registry_enabled! + before_action do + push_frontend_feature_flag(:harbor_registry_integration) + end + + def show + render :index + end + + private + + def harbor_registry_enabled! + render_404 unless Feature.enabled?(:harbor_registry_integration) + end + end + end +end diff --git a/app/controllers/projects/harbor/application_controller.rb b/app/controllers/projects/harbor/application_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..e6e694783fa0d61805ff8d2b35eaf7856b0ff572 --- /dev/null +++ b/app/controllers/projects/harbor/application_controller.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Projects + module Harbor + class ApplicationController < Projects::ApplicationController + layout 'project' + + before_action :harbor_registry_enabled! + before_action do + push_frontend_feature_flag(:harbor_registry_integration) + end + + feature_category :integrations + + private + + def harbor_registry_enabled! + render_404 unless Feature.enabled?(:harbor_registry_integration) + end + end + end +end diff --git a/app/controllers/projects/harbor/repositories_controller.rb b/app/controllers/projects/harbor/repositories_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..dd3e3dc19781e416b189e582c3d97d42a1044efc --- /dev/null +++ b/app/controllers/projects/harbor/repositories_controller.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Projects + module Harbor + class RepositoriesController < ::Projects::Harbor::ApplicationController + def show + render :index + end + end + end +end diff --git a/app/views/groups/harbor/repositories/index.html.haml b/app/views/groups/harbor/repositories/index.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..1ee15557e21a8730d86c1834131251681632f15c --- /dev/null +++ b/app/views/groups/harbor/repositories/index.html.haml @@ -0,0 +1,9 @@ +- page_title _("Harbor Registry") +- @content_class = "limit-container-width" unless fluid_layout + +#js-harbor-registry-list-group{ data: { endpoint: group_harbor_registries_path(@group), + "no_containers_image" => image_path('illustrations/docker-empty-state.svg'), + "containers_error_image" => image_path('illustrations/docker-error-state.svg'), + "help_page_path" => help_page_path('user/packages/container_registry/index'), + connection_error: (!!@connection_error).to_s, + invalid_path_error: (!!@invalid_path_error).to_s, } } diff --git a/app/views/projects/harbor/repositories/index.html.haml b/app/views/projects/harbor/repositories/index.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..b3f5b91596d461386fbb412feed7ff8dac87e783 --- /dev/null +++ b/app/views/projects/harbor/repositories/index.html.haml @@ -0,0 +1,9 @@ +- page_title _("Harbor Registry") +- @content_class = "limit-container-width" unless fluid_layout + +#js-harbor-registry-list-project{ data: { endpoint: project_harbor_registry_index_path(@project), + "no_containers_image" => image_path('illustrations/docker-empty-state.svg'), + "containers_error_image" => image_path('illustrations/docker-error-state.svg'), + "help_page_path" => help_page_path('user/packages/container_registry/index'), + connection_error: (!!@connection_error).to_s, + invalid_path_error: (!!@invalid_path_error).to_s, } } diff --git a/config/feature_flags/development/harbor_registry_integration.yml b/config/feature_flags/development/harbor_registry_integration.yml new file mode 100644 index 0000000000000000000000000000000000000000..84d9709ca30bf7f4c08d49a1b2911972cffd843f --- /dev/null +++ b/config/feature_flags/development/harbor_registry_integration.yml @@ -0,0 +1,8 @@ +--- +name: harbor_registry_integration +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81593 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/353595 +milestone: '14.9' +type: development +group: group::package +default_enabled: false diff --git a/config/routes/group.rb b/config/routes/group.rb index 41a165ab6e6c949bd7939ba99d438ebe439edf59..fecd3135cba85f666a35014ce9d9dbbabceb6ceb 100644 --- a/config/routes/group.rb +++ b/config/routes/group.rb @@ -118,6 +118,7 @@ end resources :container_registries, only: [:index, :show], controller: 'registry/repositories' + resources :harbor_registries, only: [:index, :show], controller: 'harbor/repositories' resource :dependency_proxy, only: [:show, :update] resources :email_campaigns, only: :index diff --git a/config/routes/project.rb b/config/routes/project.rb index ccead5376fe3432cd1d75e793e22a49d4eb3efa6..eae30d3408afd4c8a935c6165b20b828ce0aeff8 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -545,6 +545,9 @@ resources :container_registry, only: [:index, :destroy, :show], # rubocop: disable Cop/PutProjectRoutesUnderScope controller: 'registry/repositories' + resources :harbor_registry, only: [:index, :show], # rubocop: disable Cop/PutProjectRoutesUnderScope + controller: 'harbor/repositories' + namespace :registry do resources :repository, only: [] do # rubocop: disable Cop/PutProjectRoutesUnderScope # We default to JSON format in the controller to avoid ambiguity. diff --git a/ee/spec/features/groups/navbar_spec.rb b/ee/spec/features/groups/navbar_spec.rb index 4fb610ed1afa4f7bea01fe1b51040f36266b2513..79f4edd5562ef2506922ea9010e4ed5fdb7ad4b4 100644 --- a/ee/spec/features/groups/navbar_spec.rb +++ b/ee/spec/features/groups/navbar_spec.rb @@ -16,6 +16,7 @@ group.add_maintainer(user) stub_feature_flags(customer_relations: false) stub_group_wikis(false) + stub_feature_flags(harbor_registry_integration: false) sign_in(user) insert_package_nav(_('Kubernetes')) @@ -231,4 +232,16 @@ it_behaves_like 'verified navigation bar' end + + context 'when harbor registry is available' do + before do + stub_feature_flags(harbor_registry_integration: true) + + insert_harbor_registry_nav(_('Package Registry')) + + visit group_path(group) + end + + it_behaves_like 'verified navigation bar' + end end diff --git a/ee/spec/features/projects/navbar_spec.rb b/ee/spec/features/projects/navbar_spec.rb index 00383b8b4534852d8032e8b7ae7696c3e1342e62..3105e4d13bcb4be030faa09248e2f6c9412a4297 100644 --- a/ee/spec/features/projects/navbar_spec.rb +++ b/ee/spec/features/projects/navbar_spec.rb @@ -14,6 +14,7 @@ before do sign_in(user) + stub_feature_flags(harbor_registry_integration: false) insert_package_nav(_('Infrastructure')) insert_infrastructure_registry_nav insert_infrastructure_google_cloud_nav @@ -81,4 +82,16 @@ it_behaves_like 'verified navigation bar' end end + + context 'when harbor registry is available' do + before do + stub_feature_flags(harbor_registry_integration: true) + + insert_harbor_registry_nav(_('Infrastructure Registry')) + + visit project_path(project) + end + + it_behaves_like 'verified navigation bar' + end end diff --git a/lib/sidebars/groups/menus/packages_registries_menu.rb b/lib/sidebars/groups/menus/packages_registries_menu.rb index 60d91c8fd104b9d3edeafba927dbf1d23ad28b0d..4c21845ef186b56d056bd4d4c95d2ac07c55f6c0 100644 --- a/lib/sidebars/groups/menus/packages_registries_menu.rb +++ b/lib/sidebars/groups/menus/packages_registries_menu.rb @@ -8,8 +8,8 @@ class PackagesRegistriesMenu < ::Sidebars::Menu def configure_menu_items add_item(packages_registry_menu_item) add_item(container_registry_menu_item) + add_item(harbor_registry__menu_item) add_item(dependency_proxy_menu_item) - true end @@ -49,6 +49,17 @@ def container_registry_menu_item ) end + def harbor_registry__menu_item + return nil_menu_item(:harbor_registry) if Feature.disabled?(:harbor_registry_integration) + + ::Sidebars::MenuItem.new( + title: _('Harbor Registry'), + link: group_harbor_registries_path(context.group), + active_routes: { controller: 'groups/harbor/repositories' }, + item_id: :harbor_registry + ) + end + def dependency_proxy_menu_item setting_does_not_exist_or_is_enabled = !context.group.dependency_proxy_setting || context.group.dependency_proxy_setting.enabled diff --git a/lib/sidebars/projects/menus/packages_registries_menu.rb b/lib/sidebars/projects/menus/packages_registries_menu.rb index f5f0da2992eaec75e77a40e0cccf527cedd07ab2..77f09986b197967289644a9df72901956751403e 100644 --- a/lib/sidebars/projects/menus/packages_registries_menu.rb +++ b/lib/sidebars/projects/menus/packages_registries_menu.rb @@ -9,7 +9,7 @@ def configure_menu_items add_item(packages_registry_menu_item) add_item(container_registry_menu_item) add_item(infrastructure_registry_menu_item) - + add_item(harbor_registry__menu_item) true end @@ -65,6 +65,17 @@ def infrastructure_registry_menu_item ) end + def harbor_registry__menu_item + return ::Sidebars::NilMenuItem.new(item_id: :harbor_registry) if Feature.disabled?(:harbor_registry_integration) + + ::Sidebars::MenuItem.new( + title: _('Harbor Registry'), + link: project_harbor_registry_index_path(context.project), + active_routes: { controller: :harbor_registry }, + item_id: :harbor_registry + ) + end + def packages_registry_disabled? !::Gitlab.config.packages.enabled || !can?(context.current_user, :read_package, context.project) end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 257a69adfd6a70f9e43f7cbb917dcac193c87591..ac015d382a75b53e1725b8fa296fda01be5b3e77 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -17932,6 +17932,9 @@ msgstr "" msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}" msgstr "" +msgid "Harbor Registry" +msgstr "" + msgid "Hashed Storage must be enabled to use Geo" msgstr "" diff --git a/spec/features/groups/navbar_spec.rb b/spec/features/groups/navbar_spec.rb index c5d2f5e6733023bc0bc3be8e92cd9bf7cb3f38ff..42b53d941c2d1d41e58db2693190812f90fe65dc 100644 --- a/spec/features/groups/navbar_spec.rb +++ b/spec/features/groups/navbar_spec.rb @@ -18,6 +18,7 @@ stub_feature_flags(customer_relations: false) stub_config(dependency_proxy: { enabled: false }) stub_config(registry: { enabled: false }) + stub_feature_flags(harbor_registry_integration: false) stub_group_wikis(false) group.add_maintainer(user) sign_in(user) @@ -70,4 +71,16 @@ it_behaves_like 'verified navigation bar' end + + context 'when harbor registry is available' do + before do + stub_feature_flags(harbor_registry_integration: true) + + insert_harbor_registry_nav(_('Package Registry')) + + visit group_path(group) + end + + it_behaves_like 'verified navigation bar' + end end diff --git a/spec/features/projects/navbar_spec.rb b/spec/features/projects/navbar_spec.rb index 91e643ff258b33372b3680c43c04584c524ab064..5098908857a3cd7f8a016b7dd88b45062fb3b3f3 100644 --- a/spec/features/projects/navbar_spec.rb +++ b/spec/features/projects/navbar_spec.rb @@ -16,6 +16,7 @@ sign_in(user) stub_config(registry: { enabled: false }) + stub_feature_flags(harbor_registry_integration: false) insert_package_nav(_('Infrastructure')) insert_infrastructure_registry_nav insert_infrastructure_google_cloud_nav @@ -76,4 +77,16 @@ it_behaves_like 'verified navigation bar' end + + context 'when harbor registry is available' do + before do + stub_feature_flags(harbor_registry_integration: true) + + insert_harbor_registry_nav(_('Infrastructure Registry')) + + visit project_path(project) + end + + it_behaves_like 'verified navigation bar' + end end diff --git a/spec/lib/sidebars/groups/menus/packages_registries_menu_spec.rb b/spec/lib/sidebars/groups/menus/packages_registries_menu_spec.rb index bc1fa3e88ff72e944cbfb6b078c391058bd9f87a..d3cb18222b59108061d42db4e21151179ff19b7b 100644 --- a/spec/lib/sidebars/groups/menus/packages_registries_menu_spec.rb +++ b/spec/lib/sidebars/groups/menus/packages_registries_menu_spec.rb @@ -23,6 +23,7 @@ context 'when menu does not have any menu item to show' do it 'returns false' do + stub_feature_flags(harbor_registry_integration: false) stub_container_registry_config(enabled: false) stub_config(packages: { enabled: false }) stub_config(dependency_proxy: { enabled: false }) @@ -35,11 +36,13 @@ describe '#link' do let(:registry_enabled) { true } let(:packages_enabled) { true } + let(:harbor_registry_integration) { true } before do stub_container_registry_config(enabled: registry_enabled) stub_config(packages: { enabled: packages_enabled }) stub_config(dependency_proxy: { enabled: true }) + stub_feature_flags(harbor_registry_integration: harbor_registry_integration) end subject { menu.link } @@ -60,8 +63,16 @@ context 'when Container Registry is not visible' do let(:registry_enabled) { false } - it 'menu link points to Dependency Proxy page' do - expect(subject).to eq find_menu(menu, :dependency_proxy).link + it 'menu link points to Harbor Registry page' do + expect(subject).to eq find_menu(menu, :harbor_registry).link + end + + context 'when Harbor Registry is not visible' do + let(:harbor_registry_integration) { false } + + it 'menu link points to Dependency Proxy page' do + expect(subject).to eq find_menu(menu, :dependency_proxy).link + end end end end @@ -175,6 +186,26 @@ it_behaves_like 'the menu entry is not available' end end + + describe 'Harbor Registry' do + let(:item_id) { :harbor_registry } + + before do + stub_feature_flags(harbor_registry_integration: harbor_registry_enabled) + end + + context 'when config harbor registry setting is disabled' do + let(:harbor_registry_enabled) { false } + + it_behaves_like 'the menu entry is not available' + end + + context 'when config harbor registry setting is enabled' do + let(:harbor_registry_enabled) { true } + + it_behaves_like 'the menu entry is available' + end + end end private diff --git a/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb b/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb index afe0b2a89511e5750eb0ff92e09eb9d099df9deb..9b78fc807bf8b348df539a6a45e46f4a260d43d2 100644 --- a/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb +++ b/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb @@ -33,6 +33,7 @@ before do stub_container_registry_config(enabled: registry_enabled) stub_config(packages: { enabled: packages_enabled }) + stub_feature_flags(harbor_registry_integration: false) end context 'when Packages Registry is visible' do @@ -144,5 +145,25 @@ end end end + + describe 'Harbor Registry' do + let(:item_id) { :harbor_registry } + + context 'when config harbor registry setting is disabled' do + it 'does not add the menu item to the list' do + stub_feature_flags(harbor_registry_integration: false) + + is_expected.to be_nil + end + end + + context 'when config harbor registry setting is enabled' do + it 'the menu item is added to list of menu items' do + stub_feature_flags(harbor_registry_integration: true) + + is_expected.not_to be_nil + end + end + end end end diff --git a/spec/requests/groups/harbor/repositories_controller_spec.rb b/spec/requests/groups/harbor/repositories_controller_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..3e475dc410e3b65ba0c15a2ece8615694b4cc9d0 --- /dev/null +++ b/spec/requests/groups/harbor/repositories_controller_spec.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Groups::Harbor::RepositoriesController do + let_it_be(:group, reload: true) { create(:group) } + let_it_be(:user) { create(:user) } + + shared_examples 'responds with 404 status' do + it 'returns 404' do + subject + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + shared_examples 'responds with 200 status' do + it 'renders the index template' do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template(:index) + end + end + + before do + stub_feature_flags(harbor_registry_integration: true) + group.add_reporter(user) + login_as(user) + end + + describe 'GET #index' do + subject do + get group_harbor_registries_path(group) + response + end + + context 'with harbor registry feature flag enabled' do + it_behaves_like 'responds with 200 status' + end + + context 'with harbor registry feature flag disabled' do + before do + stub_feature_flags(harbor_registry_integration: false) + end + + it_behaves_like 'responds with 404 status' + end + end + + describe 'GET #show' do + subject do + get group_harbor_registry_path(group, 1) + response + end + + context 'with harbor registry feature flag enabled' do + it_behaves_like 'responds with 200 status' + end + + context 'with harbor registry feature flag disabled' do + before do + stub_feature_flags(harbor_registry_integration: false) + end + + it_behaves_like 'responds with 404 status' + end + end +end diff --git a/spec/requests/projects/harbor/repositories_controller_spec.rb b/spec/requests/projects/harbor/repositories_controller_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..cdb5a696d7ea8fab982defeb2a0ae1e028322933 --- /dev/null +++ b/spec/requests/projects/harbor/repositories_controller_spec.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::Harbor::RepositoriesController do + let_it_be(:project, reload: true) { create(:project) } + let_it_be(:user) { create(:user) } + + shared_examples 'responds with 404 status' do + it 'returns 404' do + subject + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + shared_examples 'responds with 200 status' do + it 'renders the index template' do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template(:index) + end + end + + before do + stub_feature_flags(harbor_registry_integration: true) + project.add_developer(user) + sign_in(user) + end + + describe 'GET #index' do + subject do + get project_harbor_registry_index_path(project) + response + end + + context 'with harbor registry feature flag enabled' do + it_behaves_like 'responds with 200 status' + end + + context 'with harbor registry feature flag disabled' do + before do + stub_feature_flags(harbor_registry_integration: false) + end + + it_behaves_like 'responds with 404 status' + end + end + + describe 'GET #show' do + subject do + get project_harbor_registry_path(project, 1) + response + end + + context 'with harbor registry feature flag enabled' do + it_behaves_like 'responds with 200 status' + end + + context 'with harbor registry feature flag disabled' do + before do + stub_feature_flags(harbor_registry_integration: false) + end + + it_behaves_like 'responds with 404 status' + end + end +end diff --git a/spec/support/helpers/navbar_structure_helper.rb b/spec/support/helpers/navbar_structure_helper.rb index 6fa69cbd6ad02df0305914d8c5d142ba270021ee..fb06ebfdae27359109678464a841bf0c6f5fc445 100644 --- a/spec/support/helpers/navbar_structure_helper.rb +++ b/spec/support/helpers/navbar_structure_helper.rb @@ -77,6 +77,14 @@ def insert_infrastructure_registry_nav ) end + def insert_harbor_registry_nav(within) + insert_after_sub_nav_item( + within, + within: _('Packages & Registries'), + new_sub_nav_item_name: _('Harbor Registry') + ) + end + def insert_infrastructure_google_cloud_nav insert_after_sub_nav_item( _('Terraform'),