Ejemplo n.º 1
0
class BaseDaemonProcessManagementTest(BaseDaemonLiveTestCase):

    def setUp(self):
        super(BaseDaemonProcessManagementTest, self).setUp()
        self.installer = PluginInstaller(logger=self.logger)

    def tearDown(self):
        super(BaseDaemonProcessManagementTest, self).tearDown()
        self.installer.uninstall_source(plugin=self.plugin_struct())
        self.installer.uninstall_source(plugin=self.plugin_struct(),
                                        deployment_id=DEPLOYMENT_ID)

    @property
    def daemon_cls(self):
        raise NotImplementedError('Must be implemented by sub-class')

    def create_daemon(self, **attributes):
        name = utils.internal.generate_agent_name()

        params = {
            'rest_host': '127.0.0.1',
            'broker_ip': '127.0.0.1',
            'user': self.username,
            'workdir': self.temp_folder,
            'logger': self.logger,
            'name': name,
            'queue': '{0}-queue'.format(name),
            'local_rest_cert_file': self._rest_cert_path,
            'broker_ssl_enabled': False,  # No SSL on the CI machines
        }
        params.update(attributes)

        factory = DaemonFactory()
        daemon = self.daemon_cls(**params)
        factory.save(daemon)
        self.addCleanup(factory.delete, daemon.name)
        self.daemons.append(daemon)
        return daemon

    def test_create(self):
        daemon = self.create_daemon()
        daemon.create()

    def test_create_overwrite(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        daemon.start()
        self.wait_for_daemon_alive(daemon.queue)

        daemon.create()
        daemon.configure()
        daemon.start()

        self.wait_for_daemon_alive(daemon.queue)
        daemon.stop()
        self.wait_for_daemon_dead(daemon.queue)

    def test_configure(self):
        raise NotImplementedError('Must be implemented by sub-class')

    def test_start(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        daemon.start()

    def test_start_delete_amqp_queue(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()

        # this creates the queue
        daemon.start()

        daemon.stop()
        daemon.start(delete_amqp_queue=True)

    @patch_get_source
    def test_start_with_error(self):
        if os.name == 'nt':
            log_dir = 'H:\\WATT_NONEXISTENT_DIR\\lo'
        else:
            log_dir = '/root/no_permission'
        daemon = self.create_daemon(log_dir=log_dir)
        daemon.create()
        daemon.configure()
        try:
            daemon.start(timeout=5)
            self.fail('Expected start operation to fail due to bad log_dir')
        except exceptions.DaemonError as e:
            if os.name == 'nt':
                # windows messages vary, and will be escaped, so let's just
                # check that the dir name is there
                expected_error = 'WATT_NONEXISTENT_DIR'
            else:
                expected_error = "Permission denied: '{0}"
            self.assertIn(expected_error.format(log_dir), str(e))

    def test_start_short_timeout(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        try:
            daemon.start(timeout=-1)
        except exceptions.DaemonStartupTimeout as e:
            self.assertTrue('failed to start in -1 seconds' in str(e))

    def test_status(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        self.assertFalse(daemon.status())
        daemon.start()
        self.assertTrue(daemon.status())

    def test_stop(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        daemon.start()
        daemon.stop()
        self.wait_for_daemon_dead(daemon.queue)

    def test_stop_short_timeout(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        daemon.start()
        try:
            daemon.stop(timeout=-1)
        except exceptions.DaemonShutdownTimeout as e:
            self.assertTrue('failed to stop in -1 seconds' in str(e))

    @patch_get_source
    def test_restart(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        self.installer.install(self.plugin_struct())
        daemon.start()
        daemon.restart()

    def test_two_daemons(self):
        daemon1 = self.create_daemon()
        daemon1.create()
        daemon1.configure()

        daemon1.start()
        self.assert_daemon_alive(daemon1.queue)

        daemon2 = self.create_daemon()
        daemon2.create()
        daemon2.configure()

        daemon2.start()
        self.assert_daemon_alive(daemon2.queue)

    @patch_get_source
    def test_conf_env_variables(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        self.installer.install(self.plugin_struct())
        daemon.start()

        expected = {
            constants.REST_HOST_KEY: str(daemon.rest_host),
            constants.REST_PORT_KEY: str(daemon.rest_port),
            constants.MANAGER_FILE_SERVER_URL_KEY:
                'https://{0}:{1}/resources'.format(
                    daemon.rest_host,
                    daemon.rest_port
            ),
            constants.AGENT_WORK_DIR_KEY: daemon.workdir,
        }

        def _get_env_var(var):
            return self.send_task(
                task_name='mock_plugin.tasks.get_env_variable',
                queue=daemon.queue,
                kwargs={'env_variable': var})

        def _check_env_var(var, expected_value):
            _value = _get_env_var(var)
            self.assertEqual(_value, expected_value)

        for key, value in expected.iteritems():
            _check_env_var(key, value)

    @patch_get_source
    def test_extra_env(self):
        daemon = self.create_daemon()
        daemon.extra_env_path = utils.env_to_file(
            {'TEST_ENV_KEY': 'TEST_ENV_VALUE'},
            posix=os.name == 'posix'
        )
        daemon.create()
        daemon.configure()
        self.installer.install(self.plugin_struct())
        daemon.start()

        # check the env file was properly sourced by querying the env
        # variable from the daemon process. this is done by a task
        value = self.send_task(
            task_name='mock_plugin.tasks.get_env_variable',
            queue=daemon.queue,
            kwargs={'env_variable': 'TEST_ENV_KEY'})
        self.assertEqual(value, 'TEST_ENV_VALUE')

    @patch_get_source
    def test_execution_env(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        self.installer.install(self.plugin_struct())
        daemon.start()

        # check that cloudify.dispatch.dispatch 'execution_env' processing
        # works.
        # not the most ideal place for this test. but on the other hand
        # all the boilerplate is already here, so this is too tempting.
        value = self.send_task(
            task_name='mock_plugin.tasks.get_env_variable',
            queue=daemon.queue,
            kwargs={'env_variable': 'TEST_ENV_KEY2'},
            execution_env={'TEST_ENV_KEY2': 'TEST_ENV_VALUE2'})
        self.assertEqual(value, 'TEST_ENV_VALUE2')

    def test_delete(self):
        raise NotImplementedError('Must be implemented by sub-class')

    def test_delete_before_stop(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        daemon.start()
        self.assertRaises(exceptions.DaemonStillRunningException,
                          daemon.delete)

    def test_delete_before_stop_with_force(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        daemon.start()
        daemon.delete(force=True)
        self.wait_for_daemon_dead(daemon.queue)

    @patch_get_source
    def test_logging(self):
        message = 'THIS IS THE TEST MESSAGE LOG CONTENT'

        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        self.installer.install(self.plugin_struct())
        self.installer.install(self.plugin_struct(),
                               deployment_id=DEPLOYMENT_ID)
        daemon.start()

        def log_and_assert(_message, _deployment_id=None):
            self.send_task(
                task_name='mock_plugin.tasks.do_logging',
                queue=daemon.queue,
                kwargs={'message': _message},
                deployment_id=_deployment_id)

            name = _deployment_id if _deployment_id else '__system__'
            logdir = os.path.join(daemon.workdir, 'logs')
            logfile = os.path.join(logdir, '{0}.log'.format(name))
            try:
                with open(logfile) as f:
                    self.assertIn(_message, f.read())
            except IOError:
                self.logger.warning('{0} content: {1}'
                                    .format(logdir, os.listdir(logdir)))
                raise

        # Test __system__ logs
        log_and_assert(message)
        # Test deployment logs
        log_and_assert(message, DEPLOYMENT_ID)

    @staticmethod
    def plugin_struct(plugin_name='mock-plugin'):
        return {
            'source': os.path.join(resources.get_resource('plugins'),
                                   plugin_name),
            'name': PLUGIN_NAME
        }

    def send_task(self,
                  task_name,
                  queue,
                  deployment_id=None,
                  args=None,
                  kwargs=None,
                  timeout=10,
                  execution_env=None):
        cloudify_context = test_utils.op_context(task_name,
                                                 task_target=queue,
                                                 plugin_name=PLUGIN_NAME,
                                                 execution_env=execution_env,
                                                 deployment_id=deployment_id)
        kwargs = kwargs or {}
        kwargs['__cloudify_context'] = cloudify_context
        handler = amqp_client.BlockingRequestResponseHandler(exchange=queue)
        client = amqp_client.get_client()
        client.add_handler(handler)
        with client:
            task = {'cloudify_task': {'kwargs': kwargs}}
            result = handler.publish(task, routing_key='operation',
                                     timeout=timeout)
        error = result.get('error')
        if error:
            raise deserialize_known_exception(error)
        else:
            return result.get('result')
Ejemplo n.º 2
0
class BaseDaemonProcessManagementTest(BaseDaemonLiveTestCase):
    def setUp(self):
        super(BaseDaemonProcessManagementTest, self).setUp()
        self.installer = PluginInstaller(logger=self.logger)

    def tearDown(self):
        super(BaseDaemonProcessManagementTest, self).tearDown()
        self.installer.uninstall_source(plugin=self.plugin_struct())
        self.installer.uninstall_source(plugin=self.plugin_struct(),
                                        deployment_id=DEPLOYMENT_ID)

    @property
    def daemon_cls(self):
        raise NotImplementedError('Must be implemented by sub-class')

    def create_daemon(self, **attributes):
        name = utils.internal.generate_agent_name()

        params = {
            'rest_host': ['127.0.0.1'],
            'broker_ip': ['127.0.0.1'],
            'user': self.username,
            'workdir': self.temp_folder,
            'logger': self.logger,
            'name': name,
            'queue': '{0}-queue'.format(name),
            'local_rest_cert_file': self._rest_cert_path,
            'broker_ssl_enabled': False,  # No SSL on the CI machines
        }
        params.update(attributes)

        factory = DaemonFactory()
        daemon = self.daemon_cls(**params)
        factory.save(daemon)
        self.addCleanup(factory.delete, daemon.name)
        self.daemons.append(daemon)
        return daemon

    def test_create(self):
        daemon = self.create_daemon()
        daemon.create()

    def test_create_overwrite(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        daemon.start()
        self.wait_for_daemon_alive(daemon.queue)

        daemon.create()
        daemon.configure()
        daemon.start()

        self.wait_for_daemon_alive(daemon.queue)
        daemon.stop()
        self.wait_for_daemon_dead(daemon.queue)

    def test_configure(self):
        raise NotImplementedError('Must be implemented by sub-class')

    def test_start(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        daemon.start()

    def test_start_delete_amqp_queue(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()

        # this creates the queue
        daemon.start()

        daemon.stop()
        daemon.start(delete_amqp_queue=True)

    @patch_get_source
    def test_start_with_error(self):
        if os.name == 'nt':
            log_dir = 'H:\\WATT_NONEXISTENT_DIR\\lo'
        else:
            log_dir = '/root/no_permission'
        daemon = self.create_daemon(log_dir=log_dir)
        daemon.create()
        daemon.configure()
        try:
            daemon.start(timeout=5)
            self.fail('Expected start operation to fail due to bad log_dir')
        except exceptions.DaemonError as e:
            if os.name == 'nt':
                # windows messages vary, and will be escaped, so let's just
                # check that the dir name is there
                expected_error = 'WATT_NONEXISTENT_DIR'
            else:
                expected_error = "Permission denied: '{0}"
            self.assertIn(expected_error.format(log_dir), str(e))

    def test_start_short_timeout(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        try:
            daemon.start(timeout=-1)
        except exceptions.DaemonStartupTimeout as e:
            self.assertTrue('failed to start in -1 seconds' in str(e))

    def test_status(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        self.assertFalse(daemon.status())
        daemon.start()
        self.assertTrue(daemon.status())

    def test_stop(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        daemon.start()
        daemon.stop()
        self.wait_for_daemon_dead(daemon.queue)

    def test_stop_short_timeout(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        daemon.start()
        try:
            daemon.stop(timeout=-1)
        except exceptions.DaemonShutdownTimeout as e:
            self.assertTrue('failed to stop in -1 seconds' in str(e))

    @patch_get_source
    def test_restart(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        self.installer.install(self.plugin_struct())
        daemon.start()
        daemon.restart()

    def test_two_daemons(self):
        daemon1 = self.create_daemon()
        daemon1.create()
        daemon1.configure()

        daemon1.start()
        self.assert_daemon_alive(daemon1.queue)

        daemon2 = self.create_daemon()
        daemon2.create()
        daemon2.configure()

        daemon2.start()
        self.assert_daemon_alive(daemon2.queue)

    @patch_get_source
    def test_conf_env_variables(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        self.installer.install(self.plugin_struct())
        daemon.start()

        expected = {
            constants.REST_HOST_KEY:
            ','.join(daemon.rest_host),
            constants.REST_PORT_KEY:
            str(daemon.rest_port),
            constants.MANAGER_FILE_SERVER_URL_KEY:
            ','.join(
                'https://{0}:{1}/resources'.format(host, daemon.rest_port)
                for host in daemon.rest_host),
            constants.AGENT_WORK_DIR_KEY:
            daemon.workdir,
        }

        def _get_env_var(var):
            return self.send_task(
                task_name='mock_plugin.tasks.get_env_variable',
                queue=daemon.queue,
                kwargs={'env_variable': var})

        def _check_env_var(var, expected_value):
            _value = _get_env_var(var)
            self.assertEqual(_value, expected_value)

        for key, value in expected.items():
            _check_env_var(key, value)

    @patch_get_source
    def test_extra_env(self):
        daemon = self.create_daemon()
        daemon.extra_env_path = utils.env_to_file(
            {'TEST_ENV_KEY': 'TEST_ENV_VALUE'}, posix=os.name == 'posix')
        daemon.create()
        daemon.configure()
        self.installer.install(self.plugin_struct())
        daemon.start()

        # check the env file was properly sourced by querying the env
        # variable from the daemon process. this is done by a task
        value = self.send_task(task_name='mock_plugin.tasks.get_env_variable',
                               queue=daemon.queue,
                               kwargs={'env_variable': 'TEST_ENV_KEY'})
        self.assertEqual(value, 'TEST_ENV_VALUE')

    @patch_get_source
    def test_execution_env(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        self.installer.install(self.plugin_struct())
        daemon.start()

        # check that cloudify.dispatch.dispatch 'execution_env' processing
        # works.
        # not the most ideal place for this test. but on the other hand
        # all the boilerplate is already here, so this is too tempting.
        value = self.send_task(
            task_name='mock_plugin.tasks.get_env_variable',
            queue=daemon.queue,
            kwargs={'env_variable': 'TEST_ENV_KEY2'},
            execution_env={'TEST_ENV_KEY2': 'TEST_ENV_VALUE2'})
        self.assertEqual(value, 'TEST_ENV_VALUE2')

    def test_delete(self):
        raise NotImplementedError('Must be implemented by sub-class')

    def test_delete_before_stop(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        daemon.start()
        self.assertRaises(exceptions.DaemonStillRunningException,
                          daemon.delete)

    def test_delete_before_stop_with_force(self):
        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        daemon.start()
        daemon.delete(force=True)
        self.wait_for_daemon_dead(daemon.queue)

    @patch_get_source
    def test_logging(self):
        message = 'THIS IS THE TEST MESSAGE LOG CONTENT'

        daemon = self.create_daemon()
        daemon.create()
        daemon.configure()
        self.installer.install(self.plugin_struct())
        self.installer.install(self.plugin_struct(),
                               deployment_id=DEPLOYMENT_ID)
        daemon.start()

        def log_and_assert(_message, _deployment_id=None):
            self.send_task(task_name='mock_plugin.tasks.do_logging',
                           queue=daemon.queue,
                           kwargs={'message': _message},
                           deployment_id=_deployment_id)

            name = _deployment_id if _deployment_id else '__system__'
            logdir = os.path.join(daemon.workdir, 'logs')
            logfile = os.path.join(logdir, '{0}.log'.format(name))
            try:
                with open(logfile) as f:
                    self.assertIn(_message, f.read())
            except IOError:
                self.logger.warning('{0} content: {1}'.format(
                    logdir, os.listdir(logdir)))
                raise

        # Test __system__ logs
        log_and_assert(message)
        # Test deployment logs
        log_and_assert(message, DEPLOYMENT_ID)

    @staticmethod
    def plugin_struct(plugin_name='mock-plugin'):
        return {
            'source': os.path.join(resources.get_resource('plugins'),
                                   plugin_name),
            'name': PLUGIN_NAME
        }

    def send_task(self,
                  task_name,
                  queue,
                  deployment_id=None,
                  args=None,
                  kwargs=None,
                  timeout=10,
                  execution_env=None):
        cloudify_context = test_utils.op_context(task_name,
                                                 task_target=queue,
                                                 plugin_name=PLUGIN_NAME,
                                                 execution_env=execution_env,
                                                 deployment_id=deployment_id)
        kwargs = kwargs or {}
        kwargs['__cloudify_context'] = cloudify_context
        handler = amqp_client.BlockingRequestResponseHandler(queue)
        client = amqp_client.get_client()
        client.add_handler(handler)
        with client:
            task = {'cloudify_task': {'kwargs': kwargs}}
            result = handler.publish(task,
                                     routing_key='operation',
                                     timeout=timeout)
        error = result.get('error')
        if error:
            raise deserialize_known_exception(error)
        else:
            return result.get('result')