def test_json_to_yaml(self): """ Running `crossbar convert` with a YAML config file will convert it to JSON. """ cbdir = self.mktemp() os.makedirs(cbdir) config_file = os.path.join(cbdir, "config.json") with open(config_file, 'w') as f: f.write("""{ "foo": { "bar": "spam", "baz": { "foo": "cat" } } }""") main.main("crossbar", ["convert", "--config={}".format(config_file)]) self.assertIn( ("YAML formatted configuration written"), self.stdout.getvalue()) with open(os.path.join(cbdir, "config.yaml"), 'r') as f: self.assertEqual(f.read(), """foo: bar: spam baz: foo: cat """)
def _start_run(self, config, app, stdout_expected, stderr_expected, end_on): with open(self.config, "wb") as f: f.write(json.dumps(config, ensure_ascii=False).encode('utf8')) with open(self.code_location + "/myapp.py", "w") as f: f.write(app) reactor = SelectReactor() make_lc(self, reactor, end_on) # In case it hard-locks reactor.callLater(self._subprocess_timeout, reactor.stop) main.main("crossbar", ["start", "--cbdir={}".format(self.cbdir), "--logformat=syslogd"], reactor=reactor) out = self.stdout.getvalue() err = self.stderr.getvalue() for i in stdout_expected: if i not in out: self.fail("Error: '{}' not in:\n{}".format(i, out)) for i in stderr_expected: if i not in err: self.fail("Error: '{}' not in:\n{}".format(i, err))
def _start_run(self, config, app, stdout_expected, stderr_expected, end_on): with open(self.config, "wb") as f: f.write(json.dumps(config, ensure_ascii=False).encode('utf8')) with open(self.code_location + "/myapp.py", "w") as f: f.write(app) reactor = SelectReactor() make_lc(self, reactor, end_on) # In case it hard-locks reactor.callLater(self._subprocess_timeout, reactor.stop) main.main("crossbar", ["start", "--cbdir={}".format(self.cbdir), "--logformat=syslogd"], reactor=reactor) out = self.stdout.getvalue() err = self.stderr.getvalue() for i in stdout_expected: if i not in out: self.fail(u"Error: '{}' not in:\n{}".format(i, out)) for i in stderr_expected: if i not in err: self.fail(u"Error: '{}' not in:\n{}".format(i, err))
def test_json_to_yaml(self): """ Running `crossbar convert` with a YAML config file will convert it to JSON. """ cbdir = self.mktemp() os.makedirs(cbdir) config_file = os.path.join(cbdir, "config.json") with open(config_file, 'w') as f: f.write("""{ "foo": { "bar": "spam", "baz": { "foo": "cat" } } }""") main.main("crossbar", ["convert", "--config={}".format(config_file)]) self.assertIn(("YAML formatted configuration written"), self.stdout.getvalue()) with open(os.path.join(cbdir, "config.yaml"), 'r') as f: self.assertEqual(f.read(), """foo: bar: spam baz: foo: cat """)
def test_basic(self): """ Just running `crossbar version` gets us the versions. """ reactor = SelectReactor() main.main("crossbar", ["version"], reactor=reactor) self.assertIn("Crossbar.io", self.stdout.getvalue()) self.assertIn(("Twisted : \x1b[33m\x1b[1m" + twisted.version.short() + "-SelectReactor"), self.stdout.getvalue())
def test_basic(self): """ Just running `crossbar version` gets us the versions. """ reactor = SelectReactor() main.main("crossbar", ["version"], reactor=reactor) self.assertIn("Crossbar.io", self.stdout.getvalue()) self.assertIn( ("Twisted : \x1b[33m\x1b[1m" + twisted.version.short() + "-SelectReactor"), self.stdout.getvalue())
def test_debug(self): """ Running `crossbar version` will give us the versions, plus the locations of some of them. """ reactor = SelectReactor() main.main("crossbar", ["version", "--loglevel=debug"], reactor=reactor) self.assertIn("Crossbar.io", self.stdout.getvalue()) self.assertIn(("Twisted : \x1b[33m\x1b[1m" + twisted.version.short() + "-SelectReactor"), self.stdout.getvalue()) self.assertIn(("[twisted.internet.selectreactor.SelectReactor]"), self.stdout.getvalue())
def test_start(self): """ A basic start, that doesn't actually enter the reactor. """ with open(self.config, "w") as f: f.write("""{"controller": {}}""") reactor = SelectReactor() reactor.run = lambda: False main.main("crossbar", ["start", "--cbdir={}".format(self.cbdir), "--logformat=syslogd"], reactor=reactor) self.assertIn("Entering reactor event loop", self.stdout.getvalue())
def test_start(self): """ A basic start, that doesn't actually enter the reactor. """ with open(self.config, "w") as f: f.write("""{"controller": {}}""") reactor = SelectReactor() reactor.run = lambda: False main.main( "crossbar", ["start", "--cbdir={}".format(self.cbdir), "--logformat=syslogd"], reactor=reactor) self.assertIn("Entering reactor event loop", self.stdout.getvalue())
def test_invalid_json_to_yaml(self): """ Running `crossbar convert` with an invalid JSON config file will error saying it is invalid. """ cbdir = self.mktemp() os.makedirs(cbdir) config_file = os.path.join(cbdir, "config.json") with open(config_file, 'w') as f: f.write("""{{{{{{{{""") with self.assertRaises(SystemExit) as e: main.main("crossbar", ["convert", "--config={}".format(config_file)]) self.assertEqual(e.exception.args[0], 1) self.assertIn(("not seem to be proper JSON"), self.stdout.getvalue())
def test_unknown_format(self): """ Running `crossbar convert` with an unknown config file produces an error. """ cbdir = self.mktemp() os.makedirs(cbdir) config_file = os.path.join(cbdir, "config.blah") open(config_file, 'wb').close() with self.assertRaises(SystemExit) as e: main.main("crossbar", ["convert", "--config={}".format(config_file)]) self.assertEqual(e.exception.args[0], 1) self.assertIn( ("Error: configuration file needs to be '.json' or '.yaml'."), self.stdout.getvalue())
def test_debug(self): """ Running `crossbar version` will give us the versions, plus the locations of some of them. """ reactor = SelectReactor() main.main("crossbar", ["version", "--loglevel=debug"], reactor=reactor) self.assertIn("Crossbar.io", self.stdout.getvalue()) self.assertIn( ("Twisted : \x1b[33m\x1b[1m" + twisted.version.short() + "-SelectReactor"), self.stdout.getvalue()) self.assertIn( ("[twisted.internet.selectreactor.SelectReactor]"), self.stdout.getvalue())
def test_invalid_json_to_yaml(self): """ Running `crossbar convert` with an invalid JSON config file will error saying it is invalid. """ cbdir = self.mktemp() os.makedirs(cbdir) config_file = os.path.join(cbdir, "config.json") with open(config_file, 'w') as f: f.write("""{{{{{{{{""") with self.assertRaises(SystemExit) as e: main.main("crossbar", ["convert", "--config={}".format(config_file)]) self.assertEqual(e.exception.args[0], 1) self.assertIn( ("not seem to be proper JSON"), self.stdout.getvalue())
def test_fileLogging(self): """ Running `crossbar start --logtofile` will log to cbdir/node.log. """ with open(self.config, "w") as f: f.write("""{"controller": {}}""") reactor = SelectReactor() reactor.run = lambda: None main.main("crossbar", ["start", "--cbdir={}".format(self.cbdir), "--logtofile"], reactor=reactor) with open(os.path.join(self.cbdir, "node.log"), "r") as f: logFile = f.read() self.assertIn("Entering reactor event loop", logFile) self.assertEqual("", self.stderr.getvalue()) self.assertEqual("", self.stdout.getvalue())
def test_hello(self): def _check(lc, reactor): if "published to 'oncounter'" in self.stdout.getvalue(): lc.stop() try: reactor.stop() except: pass appdir = self.mktemp() cbdir = os.path.join(appdir, ".crossbar") reactor = SelectReactor() main.main("crossbar", ["init", "--appdir={}".format(appdir), "--template=hello:python"], reactor=reactor) self.assertIn("Application template initialized", self.stdout.getvalue()) reactor = SelectReactor() make_lc(self, reactor, _check) # In case it hard-locks reactor.callLater(self._subprocess_timeout, reactor.stop) main.main("crossbar", ["start", "--cbdir={}".format(cbdir.path), "--logformat=syslogd"], reactor=reactor) stdout_expected = ["published to 'oncounter'"] for i in stdout_expected: self.assertIn(i, self.stdout.getvalue())
def test_stalePID(self): with open(self.config, "w") as f: f.write("""{"controller": {}}""") with open(os.path.join(self.cbdir, "node.pid"), "w") as f: f.write("""{"pid": 9999999}""") reactor = SelectReactor() reactor.run = lambda: None main.main("crossbar", ["start", "--cbdir={}".format(self.cbdir), "--logformat=syslogd"], reactor=reactor) self.assertIn( ("Stale Crossbar.io PID file (pointing to non-existing process " "with PID {pid}) {fp} removed").format( fp=os.path.abspath(os.path.join(self.cbdir, "node.pid")), pid=9999999), self.stdout.getvalue())
def test_stalePID(self): with open(self.config, "w") as f: f.write("""{"controller": {}}""") with open(os.path.join(self.cbdir, "node.pid"), "w") as f: f.write("""{"pid": 9999999}""") reactor = SelectReactor() reactor.run = lambda: None main.main( "crossbar", ["start", "--cbdir={}".format(self.cbdir), "--logformat=syslogd"], reactor=reactor) self.assertIn( ("Stale Crossbar.io PID file (pointing to non-existing process " "with PID {pid}) {fp} removed").format(fp=os.path.abspath( os.path.join(self.cbdir, "node.pid")), pid=9999999), self.stdout.getvalue())
def test_configValidationFailure(self): """ Running `crossbar start` with an invalid config will print a warning. """ with open(self.config, "w") as f: f.write("") reactor = SelectReactor() with self.assertRaises(SystemExit) as e: main.main("crossbar", [ "start", "--cbdir={}".format(self.cbdir), "--logformat=syslogd" ], reactor=reactor) # Exit with code 1 self.assertEqual(e.exception.args[0], 1) # The proper warning should be emitted self.assertIn("*** Configuration validation failed ***", self.stderr.getvalue()) self.assertIn(("configuration file does not seem to be proper JSON "), self.stderr.getvalue())
def test_configValidationFailure(self): """ Running `crossbar start` with an invalid config will print a warning. """ with open(self.config, "w") as f: f.write("") reactor = SelectReactor() with self.assertRaises(SystemExit) as e: main.main("crossbar", ["start", "--cbdir={}".format(self.cbdir), "--logformat=syslogd"], reactor=reactor) # Exit with code 1 self.assertEqual(e.exception.args[0], 1) # The proper warning should be emitted self.assertIn("*** Configuration validation failed ***", self.stderr.getvalue()) self.assertIn(("configuration file does not seem to be proper JSON "), self.stderr.getvalue())
def run(args=None, reactor=None, personality=None): """ Main entry point into Crossbar.io: * when called with no arguments, the arguments are read from the command line * when called with an explicit arguments list, use the same arguments in the list as on the command line, leaving out the initial `crossbar` command. twisted.internet.error.ReactorNotRestartable **Examples** To start Crossbar.io programmatically from a given node directory: .. code-block:: python import crossbar crossbar.run(['start', '--cbdir', '/tmp/mynode1/.crossbar']) To start Crossbar.io with log output at level "debug" .. code-block:: console $ crossbar start --loglevel=debug To chose a specific event reactor and print version information .. code-block:: console $ CROSSBAR_REACTOR="kqueue" crossbar version To start from a specified node directory (instead of the default `$CWD/.crossbar`): .. code-block:: console $ CROSSBAR_DIR="/tmp/test/.crossbar" crossbar start which is the same as .. code-block:: console $ crossbar start --cbdir=/tmp/test/.crossbar **Influential command line options and environment variables include:** ================== ======================== ============================================== Command line arg: Environment variable: Description: ================== ======================== ============================================== n.a. **CROSSBAR_REACTOR** Event reactor: * **auto** * **select** * **poll** * **epoll** * **kqueue** * **iocp** n.a. **CROSSBAR_PERSONALITY** Node personality: * **standalone** * **edge** * **master** ``--cbdir`` **CROSSBAR_DIR** Node directory (local directory) ``--config`` **CROSSBAR_CONFIG** Node configuration (local filename) ``--color`` n.a. Enable colored terminal output ``--loglevel`` n.a. Select log level ``--logformat`` n.a. Select log format ``--logdir`` n.a. Log to this local directory ``--logtofile`` n.a. Enable logging to file ================== ======================== ============================================== .. seealso:: `TwistedMatrix: Choosing a Reactor and GUI Toolkit Integration <https://twistedmatrix.com/documents/current/core/howto/choosing-reactor.html>`_ .. note:: The Twisted reactor to use can only be explicitly chosen via an environment variable, not a command line option. :param args: The (optional) program name . :type args: List[str] :param reactor: Optional Twisted reactor to use. If none is given, try to load the optimal reactor for Crossbar.io to run on the host platform. :type reactor: :class:`twisted.internet.reactor` """ import sys import os from autobahn.twisted import install_reactor if reactor is not None and reactor not in _DEFINED_REACTORS: raise Exception('illegal value "{}" for reactor'.format(reactor)) if personality is not None and personality not in _DEFINED_PERSONALITIES: raise Exception( 'illegal value "{}" for personality. Valid: {}'.format( personality, ", ".join(_DEFINED_PERSONALITIES), ) ) # use argument list from command line if none is given explicitly if args is None: exename = os.path.basename(sys.argv[0]) args = sys.argv[1:] else: exename = 'crossbar' # IMPORTANT: keep the reactor install as early as possible to avoid importing # any Twisted module that comes with the side effect of installing a default # reactor (which might not be what we want!). if reactor is None: # we use an Autobahn utility to import the "best" available Twisted reactor reactor = install_reactor(explicit_reactor=os.environ.get('CROSSBAR_REACTOR', None), verbose=False, require_optimal_reactor=False) # Twisted reactor installed FROM HERE *** # get installed personalities _personalities = personalities() # set personality if not personality: # choose node personality to run if 'CROSSBAR_PERSONALITY' in os.environ: personality = os.environ['CROSSBAR_PERSONALITY'] if personality not in _DEFINED_PERSONALITIES: raise Exception( 'illegal value "{}" for personality (from CROSSBAR_PERSONALITY environment variable): {}'.format( personality, ", ".join(_DEFINED_PERSONALITIES), ) ) else: personality = 'standalone' if personality not in _personalities: raise Exception('fatal: no personality "{}" [{}]'.format(personality, sorted(_personalities.keys()))) # get chosen personality class personality_klass = _personalities[personality] # do NOT move this import above *** (triggers reactor imports) from crossbar.node.main import main # and now actually enter here .. this never returns! return main(exename, args, reactor, personality_klass)
def run(args=None, reactor=None, personality=None): """ Main entry point into Crossbar.io: * when called with no arguments, the arguments are read from the command line * when called with an explicit arguments list, use the same arguments in the list as on the command line, leaving out the initial `crossbar` command. twisted.internet.error.ReactorNotRestartable **Examples** To start Crossbar.io programmatically from a given node directory: .. code-block:: python import crossbar crossbar.run(['start', '--cbdir', '/tmp/mynode1/.crossbar']) To start Crossbar.io with log output at level "debug" .. code-block:: console $ crossbar start --loglevel=debug To chose a specific event reactor and print version information .. code-block:: console $ CROSSBAR_REACTOR="kqueue" crossbar version To start from a specified node directory (instead of the default `$CWD/.crossbar`): .. code-block:: console $ CROSSBAR_DIR="/tmp/test/.crossbar" crossbar start which is the same as .. code-block:: console $ crossbar start --cbdir=/tmp/test/.crossbar **Influential command line options and environment variables include:** ================== ======================== ============================================== Command line arg: Environment variable: Description: ================== ======================== ============================================== n.a. **CROSSBAR_REACTOR** Event reactor: * **auto** * **select** * **poll** * **epoll** * **kqueue** * **iocp** n.a. **CROSSBAR_PERSONALITY** Node personality: * **standalone** * **fabric** * **fabricenter** ``--cbdir`` **CROSSBAR_DIR** Node directory (local directory) ``--config`` **CROSSBAR_CONFIG** Node configuration (local filename) ``--color`` n.a. Enable colored terminal output ``--loglevel`` n.a. Select log level ``--logformat`` n.a. Select log format ``--logdir`` n.a. Log to this local directory ``--logtofile`` n.a. Enable logging to file ================== ======================== ============================================== .. seealso:: `TwistedMatrix: Choosing a Reactor and GUI Toolkit Integration <https://twistedmatrix.com/documents/current/core/howto/choosing-reactor.html>`_ .. note:: The Twisted reactor to use can only be explicitly chosen via an environment variable, not a command line option. :param args: The (optional) program name . :type args: List[str] :param reactor: Optional Twisted reactor to use. If none is given, try to load the optimal reactor for Crossbar.io to run on the host platform. :type reactor: :class:`twisted.internet.reactor` """ import sys import os from autobahn.twisted import install_reactor if reactor is not None and reactor not in _DEFINED_REACTORS: raise Exception('illegal value "{}" for reactor'.format(reactor)) if personality is not None and personality not in _DEFINED_PERSONALITIES: raise Exception('illegal value "{}" for personality'.format(personality)) # use argument list from command line if none is given explicitly if args is None: exename = os.path.basename(sys.argv[0]) args = sys.argv[1:] else: exename = 'crossbar' # IMPORTANT: keep the reactor install as early as possible to avoid importing # any Twisted module that comes with the side effect of installing a default # reactor (which might not be what we want!). if reactor is None: # we use an Autobahn utility to import the "best" available Twisted reactor reactor = install_reactor(explicit_reactor=os.environ.get('CROSSBAR_REACTOR', None), verbose=False, require_optimal_reactor=False) # Twisted reactor installed FROM HERE *** # get installed personalities _personalities = personalities() # set personality if not personality: # choose node personality to run if 'CROSSBAR_PERSONALITY' in os.environ: personality = os.environ['CROSSBAR_PERSONALITY'] if personality not in _DEFINED_PERSONALITIES: raise Exception('illegal value "{}" for personality (from CROSSBAR_PERSONALITY environment variable)'.format(personality)) else: if _SELECT_MAX_PERSONALITY_AS_DEFAULT: if 'fabriccenter' in _personalities: personality = 'fabriccenter' elif 'fabric' in _personalities: personality = 'fabric' elif 'standalone' in _personalities: personality = 'standalone' else: raise Exception('logic error') else: personality = 'standalone' if personality not in _personalities: raise Exception('fatal: no personality "{}"'.format(personality)) # get chosen personality class personality_klass = _personalities[personality] # do NOT move this import above *** (triggers reactor imports) from crossbar.node.main import main # and now actually enter here .. this never returns! return main(exename, args, reactor, personality_klass)