[cubicweb-ctl] move to accepting only once instance name per command
The rationals behind this decision are:
- while in the past managing all instances sytem wide made a lot of sens,
pratices have evolved today and we've moved to managing one instance by one
- this makes things easier to debug since commands since using them on several
instances were making this harder (errors hidden in the middle)
- also solve the problem of the return code to have, before it was always 0
which prevented to do things like:
ipython --pdb $(which cubicweb-ctl) $command $instance
or shell scripts that used it
- this simplify the code and is always good to take
--- a/cubicweb/cwctl.py Tue Jul 09 11:13:08 2019 +0200
+++ b/cubicweb/cwctl.py Tue May 21 16:36:12 2019 +0200
@@ -23,7 +23,7 @@
# completion). So import locally in command helpers.
import sys
from warnings import filterwarnings
-from os import listdir, system
+from os import listdir
from os.path import exists, join, isdir
try:
@@ -98,10 +98,12 @@
class InstanceCommand(Command):
- """base class for command taking 0 to n instance id as arguments
- (0 meaning all registered instances)
- """
- arguments = '[<instance>...]'
+ """base class for command taking one instance id as arguments"""
+ arguments = '<instance>'
+
+ # enforce having one instance
+ min_args = max_args = 1
+
options = (
("force",
{'short': 'f', 'action': 'store_true',
@@ -116,33 +118,9 @@
"""run the <command>_method on each argument (a list of instance
identifiers)
"""
- if not args:
- args = list_instances(cwcfg.instances_dir())
- try:
- askconfirm = not self.config.force
- except AttributeError:
- # no force option
- askconfirm = False
- else:
- askconfirm = False
- self.run_args(args, askconfirm)
+ appid = args[0]
+ cmdmeth = getattr(self, '%s_instance' % self.name)
- def run_args(self, args, askconfirm):
- status = 0
- for appid in args:
- if askconfirm:
- print('*' * 72)
- if not ASK.confirm('%s instance %r ?' % (self.name, appid)):
- continue
- try:
- status = max(status, self.run_arg(appid))
- except (KeyboardInterrupt, SystemExit):
- sys.stderr.write('%s aborted\n' % self.name)
- return 2 # specific error code
- sys.exit(status)
-
- def run_arg(self, appid):
- cmdmeth = getattr(self, '%s_instance' % self.name)
try:
status = cmdmeth(appid) or 0
except (ExecutionError, ConfigurationError) as ex:
@@ -155,30 +133,12 @@
sys.stderr.write('instance %s not %s: %s\n' % (
appid, self.actionverb, ex))
status = 8
- return status
-
-
-class InstanceCommandFork(InstanceCommand):
- """Same as `InstanceCommand`, but command is forked in a new environment
- for each argument
- """
- def run_args(self, args, askconfirm):
- if len(args) > 1:
- forkcmd = ' '.join(w for w in sys.argv if w not in args)
- else:
- forkcmd = None
- for appid in args:
- if askconfirm:
- print('*' * 72)
- if not ASK.confirm('%s instance %r ?' % (self.name, appid)):
- continue
- if forkcmd:
- status = system('%s %s' % (forkcmd, appid))
- if status:
- print('%s exited with status %s' % (forkcmd, status))
- else:
- self.run_arg(appid)
+ except (KeyboardInterrupt, SystemExit):
+ sys.stderr.write('%s aborted\n' % self.name)
+ status = 2 # specific error code
+
+ sys.exit(status)
# base commands ###############################################################
@@ -465,16 +425,15 @@
config.init_log(config['log-threshold'], force=True)
-class UpgradeInstanceCommand(InstanceCommandFork):
+class UpgradeInstanceCommand(InstanceCommand):
"""Upgrade an instance after cubicweb and/or component(s) upgrade.
For repository update, you will be prompted for a login / password to use
to connect to the system database. For some upgrades, the given user
should have create or alter table permissions.
- <instance>...
- identifiers of the instances to upgrade. If no instance is
- given, upgrade them all.
+ <instance>
+ identifier of the instance to upgrade.
"""
name = 'upgrade'
actionverb = 'upgraded'
@@ -638,6 +597,7 @@
name = 'shell'
arguments = '<instance> [batch command file(s)] [-- <script arguments>]'
min_args = 1
+ max_args = None
options = (
('system-only',
{'short': 'S', 'action': 'store_true',
@@ -701,9 +661,8 @@
class RecompileInstanceCatalogsCommand(InstanceCommand):
"""Recompile i18n catalogs for instances.
- <instance>...
- identifiers of the instances to consider. If no instance is
- given, recompile for all registered instances.
+ <instance>
+ identifier of the instance to consider.
"""
name = 'i18ninstance'
@@ -747,7 +706,7 @@
class ConfigureInstanceCommand(InstanceCommand):
"""Configure instance.
- <instance>...
+ <instance>
identifier of the instance to configure.
"""
name = 'configure'
--- a/cubicweb/test/unittest_cwctl.py Tue Jul 09 11:13:08 2019 +0200
+++ b/cubicweb/test/unittest_cwctl.py Tue May 21 16:36:12 2019 +0200
@@ -20,17 +20,20 @@
from os.path import join
from io import StringIO
import unittest
-from unittest.mock import patch
+from unittest.mock import patch, MagicMock
-from cubicweb.cwctl import ListCommand
+from logilab.common.clcommands import CommandLine
+
+from cubicweb.cwctl import ListCommand, InstanceCommand
from cubicweb.devtools.testlib import CubicWebTC
from cubicweb.server.migractions import ServerMigrationHelper
+from cubicweb.cwconfig import CubicWebConfiguration as cwcfg
+from cubicweb.__pkginfo__ import version as cw_version
import unittest_cwconfig
class CubicWebCtlTC(unittest.TestCase):
-
setUpClass = unittest_cwconfig.CubicWebConfigurationTC.setUpClass
tearDownClass = unittest_cwconfig.CubicWebConfigurationTC.tearDownClass
@@ -78,5 +81,32 @@
mih.cmd_process_script(scriptname, None, scriptargs=args)
+class TestCommand(InstanceCommand):
+ "I need some doc"
+ name = "test"
+
+ def test_instance(self, appid):
+ pass
+
+
+class InstanceCommandTest(unittest.TestCase):
+ def test_getting_called(self):
+ CWCTL = CommandLine('cubicweb-ctl', 'The CubicWeb swiss-knife.',
+ version=cw_version, check_duplicated_command=False)
+ cwcfg.load_cwctl_plugins()
+ CWCTL.register(TestCommand)
+
+ TestCommand.test_instance = MagicMock(return_value=0)
+
+ # pretend that this instance exists
+ cwcfg.config_for = MagicMock(return_value=object())
+
+ try:
+ CWCTL.run(["test", "some_instance"])
+ except SystemExit as ex: # CWCTL will finish the program after that
+ self.assertEqual(ex.code, 0)
+ TestCommand.test_instance.assert_called_with("some_instance")
+
+
if __name__ == '__main__':
unittest.main()
--- a/doc/changes/3.27.rst Tue Jul 09 11:13:08 2019 +0200
+++ b/doc/changes/3.27.rst Tue May 21 16:36:12 2019 +0200
@@ -40,6 +40,9 @@
one can expect tests that seem to pass (but were actually silently failing)
to fail now.
+* All "cubicweb-ctl" command only accept one instance argument from now one
+ (instead of 0 to n)
+
Deprecated code drops
---------------------