class RobotContext: def __init__(self, god): self.god = god def __enter__(self): j.clients.zrobot.get('test', {'url': 'http://localhost:6600'}) self.cl = ZeroRobotManager('test') self.robot = Robot() self.robot.set_data_repo(j.sal.fs.getTmpDirPath()) self.robot.add_template_repo('http://github.com/threefoldtech/0-robot', directory='tests/fixtures/templates') if os.path.exists(config.data_repo.path): shutil.rmtree(config.data_repo.path) # make sure we don't have any service loaded scol.drop_all() self.robot.start(listen='127.0.0.1:6600', block=False, testing=True, god=self.god) return self.cl def __exit__(self, type, value, tb): self.robot.stop(timeout=0) if os.path.exists(config.data_repo.path): shutil.rmtree(config.data_repo.path) j.clients.zrobot.delete('test')
def start(listen, data_repo, template_repo, config_repo, config_key, debug, telegram_bot_token, telegram_chat_id, auto_push, auto_push_interval, admin_organization, user_organization, mode, god): """ start the 0-robot daemon. this will start the REST API on address and port specified by --listen and block """ level = "INFO" if debug: level = "DEBUG" j.logger.handlers_level_set(level) j.logger.loggers_level_set(level) # Check if configmanager is configured to zdb backend and has a namespace configured if j.core.state.configGetFromDict("myconfig", "backend", "file") == "db": namespace = j.core.state.configGetFromDict("myconfig", "namespace", "default") if namespace: j.tools.configmanager.set_namespace(namespace) else: raise RuntimeError( "Working in zdb backend mode and don't have a namespace") if (telegram_bot_token and not telegram_chat_id) or (telegram_chat_id and not telegram_bot_token): raise ValueError( "To enable telegram error logging, you need to specify both the --telegram-bot-token and the --telegram-chat-id options" ) if telegram_bot_token: telegram_logger.disabled = False telegrambot = j.clients.telegram_bot.get( instance='errorbot', data=dict(bot_token_=telegram_bot_token)) handler = TelegramHandler(telegrambot, telegram_chat_id) handler.setFormatter(TelegramFormatter()) handler.setLevel(logging.ERROR) telegram_logger.addHandler(handler) robot = Robot() config_repo = config_repo or get_db_config_repo() config_key = config_key or get_db_config_key() robot.set_config_repo(config_repo, config_key) robot.set_data_repo(data_repo) for url in template_repo: robot.add_template_repo(url) robot.start(listen=listen, auto_push=auto_push, auto_push_interval=auto_push_interval, admin_organization=admin_organization, user_organization=user_organization, mode=mode, god=god)
def new(id, with_tmpl): robot = Robot() robot.set_data_repo(j.sal.fs.getTmpDirPath()) if with_tmpl: robot.add_template_repo('http://github.com/threefoldtech/0-robot', directory='tests/fixtures/templates') listen = "localhost:660%d" % int(id) addr = "http://%s" % listen robot.start(listen=listen, testing=True)
def start(listen, data_repo, template_repo, config_repo, config_key, debug, telegram_bot_token, telegram_chat_id, auto_push, auto_push_interval, admin_organization, user_organization, mode, god): """ start the 0-robot daemon. this will start the REST API on address and port specified by --listen and block """ level = "INFO" if debug: level = "DEBUG" j.logger.handlers_level_set(level) j.logger.loggers_level_set(level) if (telegram_bot_token and not telegram_chat_id) or (telegram_chat_id and not telegram_bot_token): raise ValueError( "To enable telegram error logging, you need to specify both the --telegram-bot-token and the --telegram-chat-id options" ) if telegram_bot_token: telegram_logger.disabled = False telegrambot = j.clients.telegram_bot.get( instance='errorbot', data=dict(bot_token_=telegram_bot_token)) handler = TelegramHandler(telegrambot, telegram_chat_id) handler.setFormatter(TelegramFormatter()) handler.setLevel(logging.ERROR) telegram_logger.addHandler(handler) robot = Robot() for url in template_repo: robot.add_template_repo(url) robot.set_data_repo(data_repo) robot.set_config_repo(config_repo, config_key) robot.start(listen=listen, auto_push=auto_push, auto_push_interval=auto_push_interval, admin_organization=admin_organization, user_organization=user_organization, mode=mode, god=god)
class TestZRobotClient(unittest.TestCase): def setUp(self): j.clients.zrobot.get('test', {'url': 'http://localhost:6600'}) self.cl = ZeroRobotManager('test') self.robot = Robot() self.robot.set_data_repo(j.sal.fs.getTmpDirPath()) self.robot.add_template_repo('http://github.com/threefoldtech/0-robot', directory='tests/fixtures/templates') if os.path.exists(config.data_repo.path): shutil.rmtree(config.data_repo.path) # make sure we don't have any service loaded scol.drop_all() self.robot.start(listen='127.0.0.1:6600', block=False, testing=True) def tearDown(self): self.robot.stop(timeout=0) if os.path.exists(config.data_repo.path): shutil.rmtree(config.data_repo.path) # make sure we don't have any service loaded scol.drop_all() j.clients.zrobot.delete('test') def test_list_templates(self): uids = [str(x) for x in self.cl.templates.uids] self.assertEqual(len(uids), 6, "number of templates should be 6") self.assertIn('github.com/threefoldtech/0-robot/node/0.0.1', uids) self.assertIn('github.com/threefoldtech/0-robot/vm/0.0.1', uids) def test_service_create(self): with self.assertRaises(TemplateNotFoundError, msg='TemplateNotFoundError should be raise\ if trying to create service from a non existing template' ): self.cl.services.create( 'github.com/threefoldtech/0-robot/notexits/0.0.1', 'foo') data = {'ip': '127.0.0.1'} node = self.cl.services.create( 'github.com/threefoldtech/0-robot/node/0.0.1', 'node1', data) self.assertEqual(type(node), ServiceProxy, 'service type should be ServiceProxy') # ensure the services actually exists scol.get_by_name(node.name) node = scol.get_by_guid(node.guid) self.assertEqual( node.data['ip'], data['ip'], "data send during creation of the service should be set to the actual service" ) self.assertEqual(len(self.cl.services.names), 1, "listing of service per name should return 1") self.assertEqual(len(self.cl.services.guids), 1, "listing of service per guid should return 1") def test_service_create_without_name(self): data = {'ip': '127.0.0.1'} node = self.cl.services.create( 'github.com/threefoldtech/0-robot/node/0.0.1', data=data) self.assertEqual(type(node), ServiceProxy, 'service type should be ServiceProxy') self.assertEqual( node.name, node.guid, "service name should be equal to service guid when created without name" ) def test_service_creation_data_race(self): def get_client(): return self.cl def create_service(name): robot = get_client() data = {'ip': '127.0.0.1'} service = self.cl.services.create( 'github.com/threefoldtech/0-robot/node/0.0.1', name, data=data) print("service %s created", name) N = 200 group = pool.Group() expected_secrets = ["greenlet_%d" % i for i in range(N)] group.map(create_service, expected_secrets) group.join() robot = get_client() assert len(robot.services.find()) == N
def start(self): """ start the 0-robot daemon. this will start the REST API on address and port specified by --listen and block Arguments: listen {string} -- listen address (default :6600) data_repo {string} -- URL of the git repository or absolute path where to save the data of the zero robot template_repo {list} -- list of template repository URL. Use fragment URL to specify a branch: http://github.com/account/repo#branch' config_repo {string} -- URL of the configuration repository (https://github.com/Jumpscale/core9/blob/development/docs/config/configmanager.md) config_key {string} -- Absolute path to ssh key to secure configuration data, which is committed and pushed (see auto-push) in the configuration repo debug {boolean} -- enable debug logging telegram_bot_token {string} -- Bot to push template action failures telegram_chat_id {string} -- Chat id to push template action failures auto_push {boolean} -- enable automatically commit and pushing of data repository auto_push_interval {integer} -- interval in minutes of automatic pushing of data repository organization {string} -- if specified, enable JWT authentication for each request Raises: ValueError -- when telegram_bot is enable but no chat id is specified """ data_repo = self.config.data['data_repo'] listen = self.config.data.get('listen', ':6600') template_repo = self.config.data.get('template_repo') config_repo = self.config.data.get('config_repo') or None config_key = self.config.data.get('config_key') or None debug = self.config.data.get('debug', False) telegram_bot_token = self.config.data.get('telegram_bot_token') or None telegram_chat_id = self.config.data.get('telegram_chat_id') or None auto_push = self.config.data.get('auto_push', False) auto_push_interval = self.config.data.get('auto_push_interval', False) organization = self.config.data.get('organization') or None level = "INFO" if debug: level = "DEBUG" j.logger.handlers_level_set(level) j.logger.loggers_level_set(level) if (telegram_bot_token and not telegram_chat_id) or (telegram_chat_id and not telegram_bot_token): raise ValueError( "To enable telegram error logging, you need to specify both the --telegram-bot-token and the --telegram-chat-id options" ) if telegram_bot_token: telegram_logger.disabled = False telegrambot = j.clients.telegram_bot.get( instance='errorbot', data=dict(bot_token_=telegram_bot_token)) handler = TelegramHandler(telegrambot, telegram_chat_id) handler.setFormatter(TelegramFormatter()) handler.setLevel(logging.ERROR) telegram_logger.addHandler(handler) robot = Robot() for url in template_repo or []: robot.add_template_repo(url) robot.set_data_repo(data_repo) robot.set_config_repo(config_repo, config_key) robot.start(listen=listen, auto_push=auto_push, auto_push_interval=auto_push_interval, jwt_organization=organization)
class TestServiceProxy(unittest.TestCase): def setUp(self): # make sure this test instance client exists j.clients.zrobot.get('test', {'url': 'http://localhost:6600'}) self.cl = ZeroRobotManager('test') self.robot = Robot() self.robot.set_data_repo(j.sal.fs.getTmpDirPath()) self.robot.add_template_repo('http://github.com/zero-os/0-robot', directory='tests/fixtures/templates') if os.path.exists(config.data_repo.path): shutil.rmtree(config.data_repo.path) # make sure we don't have any service loaded scol.drop_all() self.robot.start(listen='127.0.0.1:6600', block=False, testing=True) def tearDown(self): self.robot.stop() if os.path.exists(config.data_repo.path): shutil.rmtree(config.data_repo.path) j.clients.zrobot.delete('test') def _create_proxy(self, public=False): template = 'github.com/zero-os/0-robot/node/0.0.1' name = 'node1' proxy = self.cl.services.create(template, name, public=public) service = scol.get_by_guid(proxy.guid) return (proxy, service) def test_create_proxy(self): proxy, service = self._create_proxy() self.assertEqual(proxy.guid, service.guid, "proxy should have same guid as real service") self.assertEqual(proxy.name, service.name, "proxy should have same name as real service") self.assertIsNone(proxy.data, "proxy should not have access to the data of the services") def test_state(self): proxy, service = self._create_proxy() service.state.set('foo', 'bar', 'ok') self.assertDictEqual(service.state.categories, proxy.state.categories, "proxy state should reflect service state") service.state.set('foo', 'bar', 'error') self.assertEqual(proxy.state.get('foo', 'bar'), {'bar': 'error'}, 'update of service state, should be reflect in proxy') proxy.state.set('foo', 'bar', 'ok') self.assertNotEqual(service.state.get('foo', 'bar'), {'bar': 'ok'}, 'update of proxy state, should not be reflect in service') def test_task_list(self): proxy, service = self._create_proxy() task = service.schedule_action('start') task.wait() all_tasks_guid = [t.guid for t in proxy.task_list.list_tasks(True)] self.assertIn(task.guid, all_tasks_guid, "task create on service should be visible from the proxy") proxy_task = proxy.task_list.get_task_by_guid(task.guid) self.assertEqual(proxy_task.state, task.state, "state of a task should be reflect on the proxy task") self.assertEqual(proxy_task.result, task.result, "result on the proxy task should be the same as on the real task") self.assertEqual(proxy_task.created, task.created, "created time on the proxy task should be the same as on the real task") self.assertEqual(proxy_task.duration, task.duration, "duration on the proxy task should be the same as on the real task") proxy.schedule_action('stop') self.assertEqual(len(service.task_list.list_tasks(all=True)), 2, "task create on proxy should be visible on real service") # test task result for None result task = service.schedule_action('stop') task.wait() self.assertEqual(task.state, TASK_STATE_OK) proxy_task = proxy.task_list.get_task_by_guid(task.guid) self.assertEqual(proxy_task.result, task.result, "result on the proxy task should be the same as on the real task") # test eco attribute on proxy tasks task = service.schedule_action('error') task.wait() proxy_task = proxy.task_list.get_task_by_guid(task.guid) self.assertIsNotNone(proxy_task.eco) self.assertEqual(proxy_task.state, TASK_STATE_ERROR) # task.wait should not raise is state is error but die is False proxy_task.wait(die=False) with self.assertRaises(ErrorConditionObject, message='task.wait should raise if state is error and die is True'): proxy_task.wait(die=True) def test_delete(self): proxy, service = self._create_proxy() proxy.delete() with self.assertRaises(KeyError, msg='deleting a proxy, should delete the real service'): scol.get_by_guid(proxy.guid) def test_task_result(self): proxy, service = self._create_proxy() for ret_val in [False, '', None]: task = proxy.schedule_action('test_return', args={'return_val': ret_val}) task.wait() assert task.result == ret_val def test_service_public(self): proxy, service = self._create_proxy(public=True) guids = list(self.cl.services.guids.keys()) assert service.guid in guids self.cl._client.config.data_set('secrets_', []) self.cl._client._api = None # force reload after we remove the secrets guids = list(self.cl.services.guids.keys()) assert service.guid in guids, "service should still be visible for client that doe not have the secret of the service" assert proxy.actions != []