From ae450111d4c96a6cacfa7c0e6a727b4263f28df0 Mon Sep 17 00:00:00 2001 From: Thorben Denzer Date: Tue, 19 Dec 2023 16:15:20 +0100 Subject: [PATCH] Fixes #36971 - GUI to allow cloning of Ansible roles from VCS --- .../api/v2/vcs_clone_controller.rb | 132 +++++++++++---- app/jobs/clone_ansible_role.rb | 2 +- app/jobs/delete_ansible_role.rb | 14 ++ app/jobs/update_ansible_role.rb | 14 ++ app/lib/proxy_api/ansible.rb | 42 ++++- app/services/foreman_ansible/vcs_cloner.rb | 8 +- config/routes.rb | 19 ++- lib/foreman_ansible/register.rb | 4 +- .../api/v2/vcs_clone_controller_test.rb | 111 ++++++++++--- .../VcsCloneModalContent.js | 156 +++++++++--------- 10 files changed, 352 insertions(+), 150 deletions(-) create mode 100644 app/jobs/delete_ansible_role.rb create mode 100644 app/jobs/update_ansible_role.rb diff --git a/app/controllers/api/v2/vcs_clone_controller.rb b/app/controllers/api/v2/vcs_clone_controller.rb index 4984c79f..64c41e57 100644 --- a/app/controllers/api/v2/vcs_clone_controller.rb +++ b/app/controllers/api/v2/vcs_clone_controller.rb @@ -9,72 +9,142 @@ class VcsCloneController < ::Api::V2::BaseController render json: { 'error' => e.message }, status: :bad_request end - rescue_from Git::GitExecuteError do |_e| + rescue_from Foreman::Exception do |_e| head :internal_server_error end - api :GET, '/vcs_clone/get_repo_info', N_('Returns information about the repo') + skip_before_action :verify_authenticity_token + + before_action :set_proxy_api + + api :GET, '/smart_proxies/:proxy_name/ansible/vcs_clone/repo_information', + N_('Queries metadata about the repo') + param :proxy_name, Array, N_('Name of the SmartProxy'), :required => true param :vcs_url, String, N_('Url of the repo'), :required => true - error 400, :desc => N_('Parameter unfulfilled') - error 500, :desc => N_('Git error') + error 400, :desc => N_('Parameter unfulfilled / invalid repo-info') def repo_information vcs_url = params.require(:vcs_url) - remote = Git.ls_remote(vcs_url).slice('head', 'branches', 'tags') - remote['vcs_url'] = vcs_url - render json: remote + render json: @proxy_api.repo_information(vcs_url) end - api :GET, '/vcs_clone/get_installed_roles', N_('Returns an array of roles installed on the provided proxy') + api :GET, '/smart_proxies/:proxy_name/ansible/vcs_clone/roles', + N_('Returns an array of roles installed on the provided proxy') formats ['json'] - param :smart_proxy, Array, N_('Name of the SmartProxy'), :required => true + param :proxy_name, Array, N_('Name of the SmartProxy'), :required => true error 400, :desc => N_('Parameter unfulfilled') - error 500, :desc => N_('Internal server error') def installed_roles - smart_proxy = params.require(:smart_proxy) - ansible_proxy = SmartProxy.find_by(name: smart_proxy) - proxy_api = find_proxy_api(ansible_proxy) - installed = proxy_api.list_installed - render json: installed - rescue Foreman::Exception - head :internal_server_error + render json: @proxy_api.list_installed end - api :POST, '/vcs_clone/install', N_('Launches a task to install the provided role') + api :POST, '/smart_proxies/:proxy_name/ansible/vcs_clone/roles', + N_('Launches a task to install the provided role') formats ['json'] param :repo_info, Hash, :desc => N_('Dictionary containing info about the role to be installed') do param :vcs_url, String, :desc => N_('Url of the repo'), :required => true param :name, String, :desc => N_('Name of the repo'), :required => true param :ref, String, :desc => N_('Branch / Tag / Commit reference'), :required => true - param :update, String, :desc => N_('Whether an existing role with the provided name should be updated'), :default => false end - param :smart_proxy, Array, N_('Array of SmartProxies the role should get installed to') + param :smart_proxy, Array, N_('SmartProxy the role should get installed to') error 400, :desc => N_('Parameter unfulfilled') - error 500, :desc => N_('Internal server error') def install_role + payload = verify_task_parameters(params) + start_vcs_task(payload, :install) + end + + api :PUT, '/smart_proxies/:proxy_name/ansible/vcs_clone/roles', + N_('Launches a task to update the provided role') + formats ['json'] + param :repo_info, Hash, :desc => N_('Dictionary containing info about the role to be installed') do + param :vcs_url, String, :desc => N_('Url of the repo'), :required => true + param :name, String, :desc => N_('Name of the repo'), :required => true + param :ref, String, :desc => N_('Branch / Tag / Commit reference'), :required => true + end + param :smart_proxy, Array, N_('SmartProxy the role should get installed to') + error 400, :desc => N_('Parameter unfulfilled') + def update_role + payload = verify_task_parameters(params) + start_vcs_task(payload, :update) + end + + api :DELETE, '/smart_proxies/:proxy_name/ansible/vcs_clone/roles/:role_name', + N_('Launches a task to delete the provided role') + formats ['json'] + param :role_name, String, :desc => N_('Name of the role that should be deleted') + param :smart_proxy, Array, N_('SmartProxy the role should get deleted from') + error 400, :desc => N_('Parameter unfulfilled') + def delete_role + payload = params.require(:role_name) + start_vcs_task(payload, :delete) + end + + private + + def set_proxy_api + unless params[:id] + msg = _('Smart proxy id is required') + return render_error('custom_error', :status => :unprocessable_entity, :locals => { :message => msg }) + end + ansible_proxy = SmartProxy.find_by(name: params[:id]) + if ansible_proxy.nil? + msg = _('Smart proxy does not exist') + return render_error('custom_error', :status => :bad_request, :locals => { :message => msg }) + else unless ansible_proxy.has_capability?('Ansible', 'vcs_clone') + msg = _('Smart proxy does not have foreman_ansible installed / is not capable of cloning from VCS') + return render_error('custom_error', :status => :bad_request, :locals => { :message => msg }) + end + end + @proxy = ansible_proxy + @proxy_api = find_proxy_api(ansible_proxy) + end + + def verify_task_parameters(params) payload = params.require(:vcs_clone). permit( repo_info: [ :vcs_url, :name, - :ref, - :update - ], - smart_proxy: [] - ) - - payload['repo_info']['update'] = false unless payload['repo_info'].key? 'update' + :ref + ] + ).to_h + %w[vcs_url name ref].each do |param| + raise ActionController::ParameterMissing.new(param) unless payload['repo_info'].key?(param) + end + payload + end - # 'smart_proxy' is an array to later allow a role to be installed on multiple SPs - ansible_proxy = SmartProxy.find_by(name: payload['smart_proxy'][0]) + def start_vcs_task(op_info, operation) + case operation + when :update + job = UpdateAnsibleRole.perform_later(op_info, @proxy) + when :install + job = CloneAnsibleRole.perform_later(op_info, @proxy) + when :delete + job = DeleteAnsibleRole.perform_later(op_info, @proxy) + else + raise Foreman::Exception.new(N_('Unsupported operation')) + end - job = CloneAnsibleRole.perform_later(payload['repo_info'], ansible_proxy) task = ForemanTasks::Task.find_by(external_id: job.provider_job_id) + render json: { task: task }, status: :ok rescue Foreman::Exception head :internal_server_error end + + private + + def set_proxy_api + unless params[:id] + msg = _('Smart proxy id is required') + return render_error('custom_error', :status => :unprocessable_entity, :locals => { :message => msg }) + end + ansible_proxy = SmartProxy.find_by(id: params[:id]) + raise Foreman::Exception.new(N_('Proxy does not support cloning from VCS')) unless ansible_proxy.has_capability?('Ansible', 'vcs_clone') + @proxy_api = find_proxy_api(ansible_proxy) + end + end end end diff --git a/app/jobs/clone_ansible_role.rb b/app/jobs/clone_ansible_role.rb index dfa2ba13..ad014bb3 100644 --- a/app/jobs/clone_ansible_role.rb +++ b/app/jobs/clone_ansible_role.rb @@ -4,7 +4,7 @@ class CloneAnsibleRole < ::ApplicationJob queue_as :default def humanized_name - 'Clone Ansible Role from VCS' + _('Clone Ansible Role from VCS') end def perform(repo_info, proxy) diff --git a/app/jobs/delete_ansible_role.rb b/app/jobs/delete_ansible_role.rb new file mode 100644 index 00000000..34f0d228 --- /dev/null +++ b/app/jobs/delete_ansible_role.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class DeleteAnsibleRole < ::ApplicationJob + queue_as :default + + def humanized_name + _('Delete Ansible Role') + end + + def perform(role_name, proxy) + vcs_cloner = ForemanAnsible::VcsCloner.new(proxy) + vcs_cloner.delete_role role_name + end +end diff --git a/app/jobs/update_ansible_role.rb b/app/jobs/update_ansible_role.rb new file mode 100644 index 00000000..9452319a --- /dev/null +++ b/app/jobs/update_ansible_role.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class UpdateAnsibleRole < ::ApplicationJob + queue_as :default + + def humanized_name + _('Update Ansible Role from VCS') + end + + def perform(repo_info, proxy) + vcs_cloner = ForemanAnsible::VcsCloner.new(proxy) + vcs_cloner.update_role repo_info + end +end diff --git a/app/lib/proxy_api/ansible.rb b/app/lib/proxy_api/ansible.rb index 316b31e7..cb856725 100644 --- a/app/lib/proxy_api/ansible.rb +++ b/app/lib/proxy_api/ansible.rb @@ -54,24 +54,60 @@ def playbooks(playbooks_names = []) raise ProxyException.new(url, e, N_('Unable to get playbooks from Ansible')) end + def repo_information(vcs_url) + parse(get("vcs_clone/repo_information?vcs_url=#{vcs_url}")) + rescue *PROXY_ERRORS, RestClient::Exception => e + raise e unless e.is_a? RestClient::RequestFailed + case e.http_code + when 400 + raise Foreman::Exception.new N_('Error requesting repo-info. Check Smartproxy log.') + else + raise e + end + end + def list_installed parse(get('vcs_clone/get_installed')) rescue *PROXY_ERRORS raise Foreman::Exception.new N_('Error requesting installed roles. Check log.') end - def vcs_clone_install(repo_info) + def install_role(repo_info) parse(post(repo_info, 'vcs_clone/install')) rescue *PROXY_ERRORS, RestClient::Exception => e raise e unless e.is_a? RestClient::RequestFailed case e.http_code when 409 - raise Foreman::Exception.new N_('A repo with the name %rName already exists.') % repo_info['repo_name'] - when 500 + raise Foreman::Exception.new N_('A repo with the name %rName already exists.') % repo_info['name'] + when 400 raise Foreman::Exception.new N_('Git Error. Check log.') else raise e end end + + def update_role(repo_info) + parse(put(repo_info, 'vcs_clone/update')) + rescue *PROXY_ERRORS, RestClient::Exception => e + raise e unless e.is_a? RestClient::RequestFailed + case e.http_code + when 400 + raise Foreman::Exception.new N_('Error updating %rName. Check Smartproxy log.') % repo_info['name'] + else + raise e + end + end + + def delete_role(role_name) + parse(delete("vcs_clone/delete/#{role_name}")) + rescue *PROXY_ERRORS, RestClient::Exception => e + raise e unless e.is_a? RestClient::RequestFailed + case e.http_code + when 400 + raise Foreman::Exception.new N_('Error deleting %rName. Check Smartproxy log.') % role_name + else + raise e + end + end end end diff --git a/app/services/foreman_ansible/vcs_cloner.rb b/app/services/foreman_ansible/vcs_cloner.rb index 9d0997a2..15f29dd7 100644 --- a/app/services/foreman_ansible/vcs_cloner.rb +++ b/app/services/foreman_ansible/vcs_cloner.rb @@ -8,8 +8,10 @@ def initialize(proxy = nil) @ansible_proxy = proxy end - def install_role(info) - proxy_api.vcs_clone_install info - end + delegate :install_role, to: :proxy_api + + delegate :update_role, to: :proxy_api + + delegate :delete_role, to: :proxy_api end end diff --git a/config/routes.rb b/config/routes.rb index 58ccf089..b7a1e6f8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -32,6 +32,17 @@ post :multiple_play_roles end end + resources :smart_proxies, :only => [] do + member do + scope '/ansible' do + get 'repo_information', to: 'vcs_clone#repo_information' + get 'roles', to: 'vcs_clone#installed_roles' + post 'roles', to: 'vcs_clone#install_role' + put 'roles', to: 'vcs_clone#update_role' + delete 'roles/:role_name', to: 'vcs_clone#delete_role', constraints: { role_name: %r{[^\/]+} } + end + end + end end end end @@ -100,14 +111,6 @@ end end - resources :vcs_clone, :only => [] do - collection do - get 'get_repo_information', to: 'vcs_clone#repo_information' - post 'install_role', to: 'vcs_clone#install_role' - get 'get_installed_roles', to: 'vcs_clone#installed_roles' - end - end - resources :ansible_override_values, :only => [:create, :destroy] resources :ansible_inventories, :only => [] do diff --git a/lib/foreman_ansible/register.rb b/lib/foreman_ansible/register.rb index aa6522d0..cbb81a59 100644 --- a/lib/foreman_ansible/register.rb +++ b/lib/foreman_ansible/register.rb @@ -161,7 +161,7 @@ permission :import_ansible_playbooks, { :'api/v2/ansible_playbooks' => [:sync, :fetch] } permission :clone_from_vcs, - { :'api/v2/vcs_clone' => [:repo_information, :installed_roles, :install_role]} + { :'api/v2/vcs_clone' => [:repo_information, :installed_roles, :install_role, :update_role, :delete_role] } end role 'Ansible Roles Manager', @@ -172,7 +172,7 @@ :import_ansible_roles, :view_ansible_variables, :view_lookup_values, :create_lookup_values, :edit_lookup_values, :destroy_lookup_values, :create_ansible_variables, :import_ansible_variables, - :edit_ansible_variables, :destroy_ansible_variables, :import_ansible_playbooks] + :edit_ansible_variables, :destroy_ansible_variables, :import_ansible_playbooks, :clone_from_vcs] role 'Ansible Tower Inventory Reader', [:view_hosts, :view_hostgroups, :view_facts, :generate_report_templates, :generate_ansible_inventory, diff --git a/test/functional/api/v2/vcs_clone_controller_test.rb b/test/functional/api/v2/vcs_clone_controller_test.rb index 9a6fcbae..264a344a 100644 --- a/test/functional/api/v2/vcs_clone_controller_test.rb +++ b/test/functional/api/v2/vcs_clone_controller_test.rb @@ -5,52 +5,109 @@ module Api module V2 class VcsCloneControllerTest < ActionController::TestCase - describe('#repo_information') do - test 'requests repo-information' do - Git.stubs(:ls_remote).returns({ 'head' => {}, 'branches' => {}, 'tags' => {} }) - - get :repo_information, params: { "vcs_url": 'https://github.com/theforeman/foreman.git' }, session: set_session_user - response = JSON.parse(@response.body) - assert_response :success - assert_equal({ 'head' => {}, - 'branches' => {}, - 'tags' => {}, - 'vcs_url' => 'https://github.com/theforeman/foreman.git' }, response) + def assert_job_invocation(response, targets) + as_admin do + targeting = JobInvocation.find(response['id']).targeting + targeting.resolve_hosts! + assert_equal [targets].flatten.sort, targeting.hosts.map(&:id).sort end + end + + describe 'input' do + test 'handles missing proxy capability' do + proxy = FactoryBot.create(:smart_proxy, :with_ansible) + + get :repo_information, + params: { id: proxy.name, vcs_url: 'https://github.com/theforeman/foreman_ansible.git' }, + session: set_session_user - test 'handles missing parameter' do - get :repo_information, params: { "vcs_urll": 'https://github.com/theforeman/foreman.git' }, session: set_session_user response = JSON.parse(@response.body) assert_response :bad_request - assert_equal({ 'error' => "param is missing or the value is empty: vcs_url\nDid you mean? vcs_urll\n vcs_clone\n controller\n action" }, - response) + assert_equal({ 'error' => + { 'message' => 'Smart proxy does not have foreman_ansible installed / is not capable of cloning from VCS' } }, response) end + end + describe '#repo_information' do + test 'requests repo information' do + proxy = FactoryBot.create(:smart_proxy, :with_ansible) + SmartProxy.any_instance.stubs(:has_capability?).returns(true) + ProxyAPI::Ansible.any_instance.expects(:repo_information).returns({ + 'head' => {}, + 'branches' => {}, + 'tags' => {}, + 'vcs_url' => 'https://github.com/theforeman/foreman_ansible.git' + }) - test 'handles exception in "GIT" module' do - Git.stubs(:ls_remote).raises(Git::GitExecuteError) + get :repo_information, + params: { id: proxy.name, vcs_url: 'https://github.com/theforeman/foreman_ansible.git' }, + session: set_session_user - get :repo_information, params: { "vcs_url": 'https://github.com/theforeman/foreman.git' }, session: set_session_user - assert_response :internal_server_error + response = JSON.parse(@response.body) + assert_response :success + assert_equal({ 'head' => {}, + 'branches' => {}, + 'tags' => {}, + 'vcs_url' => 'https://github.com/theforeman/foreman_ansible.git' }, response) end end - describe '#installed_roles' do test 'requests installed roles' do - roles_array = %w[ansible_role_1 ansible_role_2] + proxy = FactoryBot.create(:smart_proxy, :with_ansible) + SmartProxy.any_instance.stubs(:has_capability?).returns(true) + ProxyAPI::Ansible.any_instance.expects(:list_installed).returns(%w[role1 role2]) - ProxyAPI::Ansible.any_instance.expects(:list_installed).returns(roles_array) + get :installed_roles, + params: { id: proxy.name }, + session: set_session_user - get :installed_roles, params: { "smart_proxy": FactoryBot.create(:smart_proxy, :with_ansible).name }, session: set_session_user response = JSON.parse(@response.body) + assert_response :success + assert_equal(%w[role1 role2], response) + end + end + describe '#install_role' do + test 'installes a role' do + proxy = FactoryBot.create(:smart_proxy, :with_ansible) + SmartProxy.any_instance.stubs(:has_capability?).returns(true) + post :install_role, + params: { id: proxy.name, repo_info: { + 'vcs_url' => 'https://github.com/theforeman/foreman_ansible.git', + 'name' => 'best.role.ever', + 'ref' => 'master' + } }, + session: set_session_user assert_response :success - assert_equal(roles_array, response) end + test 'handles faulty parameters' do + proxy = FactoryBot.create(:smart_proxy, :with_ansible) + SmartProxy.any_instance.stubs(:has_capability?).returns(true) - test 'handles erroneous smart_proxy value' do - get :installed_roles, params: { "smart_proxy": 'something_unknown' }, session: set_session_user + post :install_role, + params: { id: proxy.name, 'repo_info': { + 'vcs_urll' => 'https://github.com/theforeman/foreman_ansible.git', + 'name' => 'best.role.ever', + 'ref' => 'master' + } }, + session: set_session_user + response = JSON.parse(@response.body) + assert_response :bad_request + assert_equal({ 'error' => 'param is missing or the value is empty: vcs_url' }, response) + end + end + describe '#update_role' do + # With the difference of the http-method being PUT, this is + # identical to #install_role + end + describe '#delete_role' do + test 'deletes a role' do + proxy = FactoryBot.create(:smart_proxy, :with_ansible) + SmartProxy.any_instance.stubs(:has_capability?).returns(true) - assert_response :internal_server_error + delete :delete_role, + params: { id: proxy.name, role_name: 'best.role.ever' }, + session: set_session_user + assert_response :success end end end diff --git a/webpack/components/VcsCloneModalContent/VcsCloneModalContent.js b/webpack/components/VcsCloneModalContent/VcsCloneModalContent.js index c09cee45..abc7eec2 100644 --- a/webpack/components/VcsCloneModalContent/VcsCloneModalContent.js +++ b/webpack/components/VcsCloneModalContent/VcsCloneModalContent.js @@ -26,10 +26,6 @@ import { } from './VcsCloneModalContentHelpers'; export const VcsCloneModalContent = () => { - const authToken = useRef( - document.querySelector('meta[name="csrf-token"]').attributes.content.value - ); - // STATE DEFINITION const [selectedItem, setSelectedItem] = useState(''); @@ -88,12 +84,7 @@ export const VcsCloneModalContent = () => { useEffect(() => { async function fetchSmartProxies() { if (isModalOpen) { - const response = await fetch('/api/smart_proxies', { - method: 'GET', - headers: { - 'X-CSRF-Token': authToken, - }, - }); + const response = await fetch('/api/smart_proxies'); const responseJson = await response.json(); const tempSmartProxies = []; responseJson.results.forEach(proxy => @@ -104,11 +95,19 @@ export const VcsCloneModalContent = () => { }) ); setSmartProxies(tempSmartProxies); + if (tempSmartProxies.length > 0) { + setSmartProxySelection([tempSmartProxies[0]]); + } else { + setAlertText( + __('No smartproxies with support for cloning from VCS found') + ); + setIsAlertVisible(true); + } } } fetchSmartProxies(); - }, [authToken, isModalOpen]); + }, [isModalOpen]); /** * Check if a role is installed when a SP is selected. @@ -132,13 +131,10 @@ export const VcsCloneModalContent = () => { */ async function getInstalledRolesAtProxy(proxy) { const response = await fetch( - `/ansible/api/v2/vcs_clone/get_installed_roles?${new URLSearchParams({ - smart_proxy: proxy, - })}`, + `/api/v2/smart_proxies/${smartProxySelection}/ansible/roles`, { method: 'GET', headers: { - 'X-CSRF-Token': authToken, Accept: 'application/json', 'Content-Type': 'application/json', }, @@ -260,23 +256,23 @@ export const VcsCloneModalContent = () => { const handleConfirmButton = useCallback(async () => { setIsModalButtonLoading(true); - const response = await fetch('api/v2/vcs_clone/install_role', { - method: 'POST', - headers: { - 'X-CSRF-Token': authToken, - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - repo_info: { - vcs_url: repoInfo.vcs_url, - name: repoName, - ref: selectedItem, - update: updateExisting, + const response = await fetch( + `/api/v2/smart_proxies/${smartProxySelection}/ansible/roles`, + { + method: updateExisting ? 'PUT' : 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', }, - smart_proxy: smartProxySelection, - }), - }); + body: JSON.stringify({ + repo_info: { + vcs_url: repoInfo.vcs_url, + name: repoName, + ref: selectedItem, + }, + }), + } + ); if (!response.ok) { showErrorToast(response.status); } else { @@ -298,42 +294,47 @@ export const VcsCloneModalContent = () => { * Called by: 'Examine'-Button * Sends a request to the server, which responds with information about the provided repository. */ - const handleExamineButton = useCallback(async gitUrl => { - const roleNameExpr = new RegExp(`^.*/(.*/.*).git$`); - const matched = roleNameExpr.exec(gitUrl); - try { - setRepoName(matched[1].replace('/', '.').toLowerCase()); - setOriginalRepoName(matched[1]); - } catch (e) { - setRepoName(__('COULD NOT EXTRACT NAME')); - } - - // TODO: Handle timeouts - setIsLoading(true); - const response = await fetch( - `/ansible/api/v2/vcs_clone/get_repo_information?${new URLSearchParams({ - vcs_url: gitUrl, - })}`, - { - method: 'GET', - headers: { - 'X-CSRF-Token': authToken, - Accept: 'application/json', - 'Content-Type': 'application/json', - }, + const handleExamineButton = useCallback( + async gitUrl => { + const roleNameExpr = new RegExp(`^.*/(.*/.*).git$`); + const matched = roleNameExpr.exec(gitUrl); + try { + setRepoName(matched[1].replace('/', '.').toLowerCase()); + setOriginalRepoName(matched[1]); + } catch (e) { + setRepoName(__('COULD NOT EXTRACT NAME')); } - ); - if (!response.ok) { - setIsLoading(false); - setAlertText(__('Could not request metadata. Use manual input.')); - setIsAlertVisible(true); - } else { - const responseJson = await response.json(); - setRepoInfo(responseJson); - setIsLoading(false); - } - }, []); + // TODO: Handle timeouts + setIsLoading(true); + + const response = await fetch( + `/api/v2/smart_proxies/${smartProxySelection}/ansible/repo_information?${new URLSearchParams( + { + vcs_url: gitUrl, + } + )}`, + { + method: 'GET', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + } + ); + + if (!response.ok) { + setIsLoading(false); + setAlertText(__('Could not request metadata. Use manual input.')); + setIsAlertVisible(true); + } else { + const responseJson = await response.json(); + setRepoInfo(responseJson); + setIsLoading(false); + } + }, + [smartProxySelection] + ); /** * To be called when a SmartProxy should be selected. @@ -378,20 +379,25 @@ export const VcsCloneModalContent = () => { * Checks whether 'Repo name' is a role that is already present on the selected SmartProxy. * -> Shows the alert if a collision is present. */ + const checkIfRoleIsInstalled = useCallback(() => { if (smartProxySelection.length !== 0) { // eslint-disable-next-line no-unused-vars for (const proxy of smartProxies) { - if (installedRoles[proxy].has(repoName) && !updateExisting) { - setAlertText( - sprintf( - __( - 'A repository with the name %(rName)s is already present on %(pName)s' - ), - { rName: repoName, pName: proxy } - ) - ); - setIsAlertVisible(true); + const roles = installedRoles[proxy]; + + if (roles !== undefined) { + if (roles.has(repoName) && !updateExisting) { + setAlertText( + sprintf( + __( + 'A repository with the name %(rName)s is already present on %(pName)s' + ), + { rName: repoName, pName: proxy } + ) + ); + setIsAlertVisible(true); + } } } }