Example #1
0
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')
Example #2
0
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)
Example #3
0
        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)
Example #4
0
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
Example #6
0
    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)
Example #7
0
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 != []