mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-10-24 02:04:04 +08:00
Merge pull request #4248 from mvdbeek/uninstall_repository_api_endpoint
Add API repository uninstall endpoint
This commit is contained in:
@@ -15,6 +15,7 @@ from galaxy.web import _future_expose_api as expose_api
|
||||
from galaxy.web.base.controller import BaseAPIController
|
||||
|
||||
from tool_shed.galaxy_install.install_manager import InstallRepositoryManager
|
||||
from tool_shed.galaxy_install.installed_repository_manager import InstalledRepositoryManager
|
||||
from tool_shed.galaxy_install.metadata.installed_repository_metadata_manager import InstalledRepositoryMetadataManager
|
||||
from tool_shed.galaxy_install.repair_repository_manager import RepairRepositoryManager
|
||||
from tool_shed.util import common_util
|
||||
@@ -616,6 +617,46 @@ class ToolShedRepositoriesController( BaseAPIController ):
|
||||
all_installed_tool_shed_repositories.extend( installed_tool_shed_repositories )
|
||||
return all_installed_tool_shed_repositories
|
||||
|
||||
@expose_api
|
||||
def uninstall_repository(self, trans, id=None, **kwd):
|
||||
"""
|
||||
DELETE /api/tool_shed_repositories/id
|
||||
DELETE /api/tool_shed_repositories/
|
||||
|
||||
:param id: encoded repository id. Either id or name, owner, changeset_revision and tool_shed_url need to be supplied
|
||||
:param kwd: 'remove_from_disk' : Remove repository from disk or deactivate repository.
|
||||
Defaults to `True` (= remove repository from disk).
|
||||
'name' : Repository name
|
||||
'owner' : Repository owner
|
||||
'changeset_revision': Changeset revision to uninstall
|
||||
'tool_shed_url' : Tool Shed URL
|
||||
"""
|
||||
if id:
|
||||
try:
|
||||
repository = repository_util.get_tool_shed_repository_by_id(self.app, id)
|
||||
except ValueError:
|
||||
raise HTTPBadRequest(detail="No repository with id '%s' found" % id)
|
||||
else:
|
||||
tsr_arguments = ['name', 'owner', 'changeset_revision', 'tool_shed_url']
|
||||
try:
|
||||
tsr_arguments = {key: kwd[key] for key in tsr_arguments}
|
||||
except KeyError as e:
|
||||
raise HTTPBadRequest(detail="Missing required parameter '%s'" % e.args[0])
|
||||
repository = repository_util.get_installed_repository(app=self.app,
|
||||
tool_shed=tsr_arguments['tool_shed_url'],
|
||||
name=tsr_arguments['name'],
|
||||
owner=tsr_arguments['owner'],
|
||||
changeset_revision=tsr_arguments['changeset_revision'])
|
||||
if not repository:
|
||||
raise HTTPBadRequest(detail="Repository not found")
|
||||
irm = InstalledRepositoryManager(app=self.app)
|
||||
errors = irm.uninstall_repository(repository=repository, remove_from_disk=kwd.get('remove_from_disk', True))
|
||||
if not errors:
|
||||
action = 'removed' if kwd.get('remove_from_disk', True) else 'deactivated'
|
||||
return {'message': 'The repository named %s has been %s.' % (repository.name, action)}
|
||||
else:
|
||||
raise Exception('Attempting to uninstall tool dependencies for repository named %s resulted in errors: %s' % (repository.name, errors))
|
||||
|
||||
@expose_api
|
||||
def repair_repository_revision( self, trans, payload, **kwd ):
|
||||
"""
|
||||
|
||||
@@ -827,12 +827,30 @@ def populate_api_routes( webapp, app ):
|
||||
action='status',
|
||||
conditions=dict( method=[ "GET" ] ) )
|
||||
|
||||
webapp.mapper.connect( 'install_repository',
|
||||
'/api/tool_shed_repositories',
|
||||
controller='tool_shed_repositories',
|
||||
action='install_repository_revision',
|
||||
conditions=dict( method=[ 'POST' ] ) )
|
||||
|
||||
webapp.mapper.connect( 'install_repository',
|
||||
'/api/tool_shed_repositories/install',
|
||||
controller='tool_shed_repositories',
|
||||
action='install',
|
||||
conditions=dict( method=[ 'POST' ] ) )
|
||||
|
||||
webapp.mapper.connect( 'tool_shed_repository',
|
||||
'/api/tool_shed_repositories',
|
||||
controller='tool_shed_repositories',
|
||||
action='uninstall_repository',
|
||||
conditions=dict( method=[ "DELETE" ]))
|
||||
|
||||
webapp.mapper.connect( 'tool_shed_repository',
|
||||
'/api/tool_shed_repositories/{id}',
|
||||
controller='tool_shed_repositories',
|
||||
action='uninstall_repository',
|
||||
conditions=dict( method=[ "DELETE" ]))
|
||||
|
||||
# Galaxy API for tool shed features.
|
||||
webapp.mapper.resource( 'tool_shed_repository',
|
||||
'tool_shed_repositories',
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from admin import AdminGalaxy
|
||||
from six import string_types
|
||||
@@ -13,8 +12,8 @@ from galaxy.tools.deps import views
|
||||
from galaxy.web.form_builder import CheckboxField
|
||||
from tool_shed.galaxy_install import dependency_display
|
||||
from tool_shed.galaxy_install import install_manager
|
||||
from tool_shed.galaxy_install.datatypes import custom_datatype_manager
|
||||
from tool_shed.galaxy_install.grids import admin_toolshed_grids
|
||||
from tool_shed.galaxy_install.installed_repository_manager import InstalledRepositoryManager
|
||||
from tool_shed.galaxy_install.metadata.installed_repository_metadata_manager import InstalledRepositoryMetadataManager
|
||||
from tool_shed.galaxy_install.repair_repository_manager import RepairRepositoryManager
|
||||
from tool_shed.galaxy_install.repository_dependencies import repository_dependency_manager
|
||||
@@ -249,84 +248,17 @@ class AdminToolshed( AdminGalaxy ):
|
||||
tool_shed_repositories = repository_util.get_installed_tool_shed_repository( trans.app, kwd[ 'id' ] )
|
||||
if not isinstance( tool_shed_repositories, list ):
|
||||
tool_shed_repositories = [tool_shed_repositories]
|
||||
irm = InstalledRepositoryManager(app=trans.app)
|
||||
for tool_shed_repository in tool_shed_repositories:
|
||||
shed_tool_conf, tool_path, relative_install_dir = \
|
||||
suc.get_tool_panel_config_tool_path_install_dir( trans.app, tool_shed_repository )
|
||||
if relative_install_dir:
|
||||
if tool_path:
|
||||
relative_install_dir = os.path.join( tool_path, relative_install_dir )
|
||||
repository_install_dir = os.path.abspath( relative_install_dir )
|
||||
else:
|
||||
repository_install_dir = None
|
||||
errors = ''
|
||||
if kwd.get( 'deactivate_or_uninstall_repository_button', False ):
|
||||
if tool_shed_repository.includes_tools_for_display_in_tool_panel:
|
||||
# Handle tool panel alterations.
|
||||
tpm = tool_panel_manager.ToolPanelManager( trans.app )
|
||||
tpm.remove_repository_contents( tool_shed_repository,
|
||||
shed_tool_conf,
|
||||
uninstall=remove_from_disk_checked )
|
||||
if tool_shed_repository.includes_data_managers:
|
||||
dmh = data_manager.DataManagerHandler( trans.app )
|
||||
dmh.remove_from_data_manager( tool_shed_repository )
|
||||
if tool_shed_repository.includes_datatypes:
|
||||
# Deactivate proprietary datatypes.
|
||||
cdl = custom_datatype_manager.CustomDatatypeLoader( trans.app )
|
||||
installed_repository_dict = cdl.load_installed_datatypes( tool_shed_repository,
|
||||
repository_install_dir,
|
||||
deactivate=True )
|
||||
if installed_repository_dict:
|
||||
converter_path = installed_repository_dict.get( 'converter_path' )
|
||||
if converter_path is not None:
|
||||
cdl.load_installed_datatype_converters( installed_repository_dict, deactivate=True )
|
||||
display_path = installed_repository_dict.get( 'display_path' )
|
||||
if display_path is not None:
|
||||
cdl.load_installed_display_applications( installed_repository_dict, deactivate=True )
|
||||
errors = irm.uninstall_repository(repository=tool_shed_repository, remove_from_disk=remove_from_disk_checked)
|
||||
action = 'uninstalled' if remove_from_disk_checked else 'deactivated'
|
||||
status = max( status, statuses.index( 'done' ) )
|
||||
message += 'The repository named <b>%s</b> has been %s. ' % (escape( tool_shed_repository.name ), action)
|
||||
if remove_from_disk_checked:
|
||||
try:
|
||||
# Remove the repository from disk.
|
||||
shutil.rmtree( repository_install_dir )
|
||||
log.debug( "Removed repository installation directory: %s" % str( repository_install_dir ) )
|
||||
removed = True
|
||||
except Exception as e:
|
||||
log.debug( "Error removing repository installation directory %s: %s" % ( str( repository_install_dir ), str( e ) ) )
|
||||
if isinstance( e, OSError ) and not os.path.exists( repository_install_dir ):
|
||||
removed = True
|
||||
log.debug( "Repository directory does not exist on disk, marking as uninstalled." )
|
||||
else:
|
||||
removed = False
|
||||
if removed:
|
||||
tool_shed_repository.uninstalled = True
|
||||
# Remove all installed tool dependencies and tool dependencies stuck in the INSTALLING state, but don't touch any
|
||||
# repository dependencies.
|
||||
tool_dependencies_to_uninstall = tool_shed_repository.tool_dependencies_installed_or_in_error
|
||||
tool_dependencies_to_uninstall.extend( tool_shed_repository.tool_dependencies_being_installed )
|
||||
for tool_dependency in tool_dependencies_to_uninstall:
|
||||
uninstalled, error_message = tool_dependency_util.remove_tool_dependency( trans.app, tool_dependency )
|
||||
if error_message:
|
||||
errors = '%s %s' % ( errors, error_message )
|
||||
tool_shed_repository.deleted = True
|
||||
if remove_from_disk_checked:
|
||||
tool_shed_repository.status = trans.install_model.ToolShedRepository.installation_status.UNINSTALLED
|
||||
tool_shed_repository.error_message = None
|
||||
if trans.app.config.manage_dependency_relationships:
|
||||
# Remove the uninstalled repository and any tool dependencies from the in-memory dictionaries in the
|
||||
# installed_repository_manager.
|
||||
trans.app.installed_repository_manager.handle_repository_uninstall( tool_shed_repository )
|
||||
else:
|
||||
tool_shed_repository.status = trans.install_model.ToolShedRepository.installation_status.DEACTIVATED
|
||||
trans.install_model.context.current.add( tool_shed_repository )
|
||||
trans.install_model.context.current.flush()
|
||||
if remove_from_disk_checked:
|
||||
message += 'The repository named <b>%s</b> has been uninstalled. ' % escape( tool_shed_repository.name )
|
||||
if errors:
|
||||
message += 'Attempting to uninstall tool dependencies resulted in errors: %s' % errors
|
||||
status = max( status, statuses.index( 'error' ) )
|
||||
else:
|
||||
status = max( status, statuses.index( 'done' ) )
|
||||
else:
|
||||
message = 'The repository named <b>%s</b> has been deactivated. ' % escape( tool_shed_repository.name )
|
||||
status = max( status, statuses.index( 'done' ) )
|
||||
status = statuses[ status ]
|
||||
if kwd.get( 'deactivate_or_uninstall_repository_button', False ):
|
||||
return trans.response.send_redirect( web.url_for( controller='admin_toolshed',
|
||||
|
||||
@@ -4,6 +4,7 @@ Class encapsulating the management of repositories installed into Galaxy from th
|
||||
import copy
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from sqlalchemy import and_, false, true
|
||||
|
||||
@@ -78,11 +79,11 @@ class InstalledRepositoryManager( object ):
|
||||
|
||||
def activate_repository( self, repository ):
|
||||
"""Activate an installed tool shed repository that has been marked as deactivated."""
|
||||
repository_clone_url = common_util.generate_clone_url_for_installed_repository( self.app, repository )
|
||||
shed_tool_conf, tool_path, relative_install_dir = suc.get_tool_panel_config_tool_path_install_dir( self.app, repository )
|
||||
repository.deleted = False
|
||||
repository.status = self.install_model.ToolShedRepository.installation_status.INSTALLED
|
||||
if repository.includes_tools_for_display_in_tool_panel:
|
||||
repository_clone_url = common_util.generate_clone_url_for_installed_repository( self.app, repository )
|
||||
tpm = tool_panel_manager.ToolPanelManager( self.app )
|
||||
irmm = InstalledRepositoryMetadataManager( app=self.app,
|
||||
tpm=tpm,
|
||||
@@ -749,6 +750,75 @@ class InstalledRepositoryManager( object ):
|
||||
if installed_repository_dict[ 'display_path' ]:
|
||||
cdl.load_installed_display_applications( installed_repository_dict, deactivate=deactivate )
|
||||
|
||||
def uninstall_repository(self, repository, remove_from_disk=True):
|
||||
errors = ''
|
||||
shed_tool_conf, tool_path, relative_install_dir = \
|
||||
suc.get_tool_panel_config_tool_path_install_dir( app=self.app, repository=repository )
|
||||
if relative_install_dir:
|
||||
if tool_path:
|
||||
relative_install_dir = os.path.join( tool_path, relative_install_dir )
|
||||
repository_install_dir = os.path.abspath( relative_install_dir )
|
||||
else:
|
||||
repository_install_dir = None
|
||||
if repository.includes_tools_for_display_in_tool_panel:
|
||||
# Handle tool panel alterations.
|
||||
tpm = tool_panel_manager.ToolPanelManager(app=self.app)
|
||||
tpm.remove_repository_contents(repository,
|
||||
shed_tool_conf,
|
||||
uninstall=remove_from_disk)
|
||||
if repository.includes_data_managers:
|
||||
dmh = data_manager.DataManagerHandler(app=self.app)
|
||||
dmh.remove_from_data_manager(repository)
|
||||
if repository.includes_datatypes:
|
||||
# Deactivate proprietary datatypes.
|
||||
cdl = custom_datatype_manager.CustomDatatypeLoader(app=self.app)
|
||||
installed_repository_dict = cdl.load_installed_datatypes( repository,
|
||||
repository_install_dir,
|
||||
deactivate=True )
|
||||
if installed_repository_dict:
|
||||
converter_path = installed_repository_dict.get( 'converter_path' )
|
||||
if converter_path is not None:
|
||||
cdl.load_installed_datatype_converters( installed_repository_dict, deactivate=True )
|
||||
display_path = installed_repository_dict.get( 'display_path' )
|
||||
if display_path is not None:
|
||||
cdl.load_installed_display_applications( installed_repository_dict, deactivate=True )
|
||||
if remove_from_disk:
|
||||
try:
|
||||
# Remove the repository from disk.
|
||||
shutil.rmtree( repository_install_dir )
|
||||
log.debug( "Removed repository installation directory: %s" % str( repository_install_dir ) )
|
||||
removed = True
|
||||
except Exception as e:
|
||||
log.debug( "Error removing repository installation directory %s: %s" % ( str( repository_install_dir ), str( e ) ) )
|
||||
if isinstance( e, OSError ) and not os.path.exists( repository_install_dir ):
|
||||
removed = True
|
||||
log.debug( "Repository directory does not exist on disk, marking as uninstalled." )
|
||||
else:
|
||||
removed = False
|
||||
if removed:
|
||||
repository.uninstalled = True
|
||||
# Remove all installed tool dependencies and tool dependencies stuck in the INSTALLING state, but don't touch any
|
||||
# repository dependencies.
|
||||
tool_dependencies_to_uninstall = repository.tool_dependencies_installed_or_in_error
|
||||
tool_dependencies_to_uninstall.extend( repository.tool_dependencies_being_installed )
|
||||
for tool_dependency in tool_dependencies_to_uninstall:
|
||||
uninstalled, error_message = tool_dependency_util.remove_tool_dependency( self.app, tool_dependency )
|
||||
if error_message:
|
||||
errors = '%s %s' % ( errors, error_message )
|
||||
repository.deleted = True
|
||||
if remove_from_disk:
|
||||
repository.status = self.app.install_model.ToolShedRepository.installation_status.UNINSTALLED
|
||||
repository.error_message = None
|
||||
if self.app.config.manage_dependency_relationships:
|
||||
# Remove the uninstalled repository and any tool dependencies from the in-memory dictionaries in the
|
||||
# installed_repository_manager.
|
||||
self.handle_repository_uninstall( repository )
|
||||
else:
|
||||
repository.status = self.app.install_model.ToolShedRepository.installation_status.DEACTIVATED
|
||||
self.app.install_model.context.current.add( repository )
|
||||
self.app.install_model.context.current.flush()
|
||||
return errors
|
||||
|
||||
def purge_repository( self, repository ):
|
||||
"""Purge a repository with status New (a white ghost) from the database."""
|
||||
sa_session = self.app.model.context.current
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
from tool_shed.galaxy_install.installed_repository_manager import InstalledRepositoryManager
|
||||
from tools.test_toolbox import BaseToolBoxTestCase
|
||||
|
||||
|
||||
class InstalledRepositoryManagerTestCase(BaseToolBoxTestCase):
|
||||
|
||||
def test_uninstall_repository(self):
|
||||
repository = self._setup_repository()
|
||||
assert repository.uninstalled is False
|
||||
irm = InstalledRepositoryManager(self.app)
|
||||
irm.uninstall_repository(repository=repository, remove_from_disk=True)
|
||||
assert repository.uninstalled is True
|
||||
|
||||
def test_deactivate_repository(self):
|
||||
self._deactivate_repository()
|
||||
|
||||
def test_activate_repository(self):
|
||||
irm, repository = self._deactivate_repository()
|
||||
irm.activate_repository(repository)
|
||||
assert repository.status == self.app.install_model.ToolShedRepository.installation_status.INSTALLED
|
||||
|
||||
def _deactivate_repository(self):
|
||||
repository = self._setup_repository()
|
||||
assert repository.uninstalled is False
|
||||
irm = InstalledRepositoryManager(self.app)
|
||||
irm.uninstall_repository(repository=repository, remove_from_disk=False)
|
||||
assert repository.uninstalled is False
|
||||
assert repository.status == self.app.install_model.ToolShedRepository.installation_status.DEACTIVATED
|
||||
return irm, repository
|
||||
|
||||
def _setup_repository(self):
|
||||
self._init_dynamic_tool_conf()
|
||||
self.app.config.tool_configs = self.config_files
|
||||
self.app.config.manage_dependency_relationships = False
|
||||
self.app.toolbox = self.toolbox
|
||||
return self._repo_install(changeset='1', config_filename=self.config_files[0])
|
||||
@@ -188,10 +188,6 @@ class ToolPanelManagerTestCase( BaseToolBoxTestCase ):
|
||||
message = message_template % ( filename, open( filename, "r" ).read() )
|
||||
raise AssertionError( message )
|
||||
|
||||
def _init_dynamic_tool_conf( self ):
|
||||
# Add a dynamic tool conf (such as a ToolShed managed one) to list of configs.
|
||||
self._add_config( """<toolbox tool_path="%s"></toolbox>""" % self.test_directory )
|
||||
|
||||
def _init_ts_tool( self, guid=DEFAULT_GUID, **kwds ):
|
||||
tool = self._init_tool( **kwds )
|
||||
tool.guid = guid
|
||||
|
||||
@@ -68,8 +68,11 @@ class BaseToolBoxTestCase( unittest.TestCase, tools_support.UsesApp, tools_supp
|
||||
self.__toolbox = None
|
||||
self.config_files = []
|
||||
|
||||
def _repo_install( self, changeset ):
|
||||
repository = tool_shed_install.ToolShedRepository()
|
||||
def _repo_install( self, changeset, config_filename=None ):
|
||||
metadata = {}
|
||||
if config_filename:
|
||||
metadata['shed_config_filename'] = config_filename
|
||||
repository = tool_shed_install.ToolShedRepository(metadata=metadata)
|
||||
repository.tool_shed = "github.com"
|
||||
repository.owner = "galaxyproject"
|
||||
repository.name = "example"
|
||||
@@ -137,6 +140,10 @@ class BaseToolBoxTestCase( unittest.TestCase, tools_support.UsesApp, tools_supp
|
||||
json.dump(content, f)
|
||||
self.config_files.append( path )
|
||||
|
||||
def _init_dynamic_tool_conf( self ):
|
||||
# Add a dynamic tool conf (such as a ToolShed managed one) to list of configs.
|
||||
self._add_config( """<toolbox tool_path="%s"></toolbox>""" % self.test_directory )
|
||||
|
||||
def _tool_conf_path( self, name="tool_conf.xml" ):
|
||||
path = os.path.join( self.test_directory, name )
|
||||
return path
|
||||
|
||||
Reference in New Issue
Block a user