Example #1
0
def main():
    arguments = parser.parse_args()

    basic_config(level=arguments.log_level,
                 log_format=arguments.log_format,
                 buffered=False)

    setproctitle(os.path.basename("[Master] %s" % sys.argv[0]))

    tcp_sock = bind_socket(address=arguments.tcp_listen,
                           port=arguments.tcp_port)

    pickle_sock = bind_socket(address=arguments.pickle_listen,
                              port=arguments.pickle_port)

    udp_sock = bind_socket(
        socket.AF_INET6 if ':' in arguments.udp_listen else socket.AF_INET,
        socket.SOCK_DGRAM,
        address=arguments.udp_listen,
        port=arguments.udp_port)

    def run():
        setproctitle(os.path.basename("[Worker] %s" % sys.argv[0]))

        storage = Storage(
            os.path.join(arguments.storage, "%03d.db" % forklib.get_id()))

        services = [
            CarbonTCPServer(sock=tcp_sock, storage=storage),
            CarbonPickleServer(sock=pickle_sock, storage=storage),
            CarbonUDPServer(sock=udp_sock, storage=storage),
            SenderService(
                proxy_url=arguments.carbon_proxy_url,
                secret=arguments.carbon_proxy_secret,
                storage=storage,
                interval=arguments.sender_interval,
                chunk_size=arguments.chunk_size,
            )
        ]

        with entrypoint(*services,
                        log_level=arguments.log_level,
                        log_format=arguments.log_format,
                        pool_size=arguments.pool_size) as loop:
            loop.set_debug(arguments.debug)
            loop.run_forever()

    if arguments.debug:
        run()
    else:
        forklib.fork(arguments.forks, run)
Example #2
0
def main():
    args = parser.parse_args()

    clear_environ(lambda i: i.startswith(ENV_VAR_PREFIX))

    # Чтобы логи не блокировали основной поток (и event loop) во время операций
    # записи в stderr или файл - логи можно буфферизовать и обрабатывать в
    # отдельном потоке (aiomisc.basic_config настроит буфферизацию
    # автоматически).
    basic_config(args.log_level, args.log_format, buffered=True)

    # Аллоцируем сокет из под привиллегированного пользователя отдельным шагом,
    # чтобы была возможность перед запуском приложения сменить пользователя ОС.
    sock = bind_socket(
        address=args.api_address,
        port=args.api_port,
        proto_name='http',
    )

    # После того как приложение аллоцировало сокет и ему больше не нужны
    # привиллегии - хорошим решением будет сменить пользователя (например,
    # на nobody, у которого нет никаких специальных привиллегий) - это также
    # усложнит жизнь злоумышленникам.
    if args.user is not None:
        logging.info('Changing user to %r', args.user.pw_name)
        os.setgid(args.user.pw_gid)
        os.setuid(args.user.pw_uid)

    # В списке процессов намного удобнее видеть название текущего приложения
    setproctitle(os.path.basename(argv[0]))

    app = create_app(args)
    run_app(app, sock=sock)
Example #3
0
def test_sdwatchdog_service(loop):
    with TemporaryDirectory(dir="/tmp") as tmp_dir:
        tmp_path = Path(tmp_dir)
        sock_path = str(tmp_path / "notify.sock")

        packets = deque()

        class FakeSystemd(UDPServer):
            def handle_datagram(self, data: bytes, addr: tuple) -> None:
                packets.append((data.decode().split("=", 1), addr))

        with bind_socket(
                socket.AF_UNIX,
                socket.SOCK_DGRAM,
                address=sock_path,
        ) as sock:
            try:
                os.environ["NOTIFY_SOCKET"] = sock_path
                os.environ["WATCHDOG_USEC"] = "100000"

                service = sdwatchdog.SDWatchdogService(
                    watchdog_interval=sdwatchdog._get_watchdog_interval(), )

                assert service.watchdog_interval == 0.1

                with aiomisc.entrypoint(
                        FakeSystemd(sock=sock),
                        service,
                        loop=loop,
                ):
                    loop.run_until_complete(asyncio.sleep(1))
            finally:
                for key in ("NOTIFY_SOCKET", "WATCHDOG_USEC"):
                    os.environ.pop(key)

    assert packets
    messages_count = Counter()
    messages = defaultdict(set)

    for (key, value), sender in packets:
        assert key
        assert value
        messages_count[key] += 1
        messages[key].add(value)

    assert 5 < messages_count["WATCHDOG"] < 25
    assert messages_count["STATUS"] == 2
    assert messages_count["WATCHDOG_USEC"] == 1
    assert tuple(messages["WATCHDOG_USEC"])[0] == "100000"
Example #4
0
def main():
    args = parser.parse_args()
    clear_environ(lambda i: i.startswith(ENV_VAR_PREFIX))

    basic_config(args.log_level, args.log_format, buffered=True)

    sock = bind_socket(address=args.api_address,
                       port=args.api_port,
                       proto_name='http')

    if args.user is not None:
        logging.info('Changing user to %r', args.user.pw_name)
        os.setgid(args.user.pw_gid)
        os.setuid(args.user.pw_uid)
    setproctitle(os.path.basename(argv[0]))

    app = create_app(args)
    run_app(app, sock=sock)
Example #5
0
def main():
    args = parser.parse_args()

    # После получения конфигурации приложения переменные окружения приложения
    # больше не нужны и даже могут представлять опасность - например, они могут
    # случайно "утечь" с отображением информации об ошибке. Злоумышленники
    # в первую очередь будут пытаться получить информацию об окружении, очистка
    # переменных окружения считается хорошим тоном.

    # Python позволяет управлять поведением stdlib модулей с помощью
    # многочисленных переменных окружения, разумно очищать переменные окружения
    # по префиксу приложения, указанного в ConfigArgParser.
    clear_environ(lambda i: i.startswith(ENV_VAR_PREFIX))

    # Чтобы логи не блокировали основной поток (и event loop) во время операций
    # записи в stderr или файл - логи можно буфферизовать и обрабатывать в
    # отдельном потоке (aiomisc.basic_config настроит буфферизацию
    # автоматически).
    basic_config(args.log_level, args.log_format, buffered=True)

    # Аллоцируем сокет из под привиллегированного пользователя отдельным шагом,
    # чтобы была возможность перед запуском приложения сменить пользователя ОС.
    sock = bind_socket(address=args.api_address,
                       port=args.api_port,
                       proto_name='http')

    # После того как приложение аллоцировало сокет и ему больше не нужны
    # привиллегии - хорошим решением будет сменить пользователя (например,
    # на nobody, у которого нет никаких специальных привиллегий) - это также
    # усложнит жизнь злоумышленникам.
    if args.user is not None:
        logging.info('Changing user to %r', args.user.pw_name)
        os.setgid(args.user.pw_gid)
        os.setuid(args.user.pw_uid)

    # В списке процессов намного удобнее видеть название текущего приложения
    setproctitle(os.path.basename(argv[0]))

    app = create_app(args)
    run_app(app, sock=sock)
    def run(self):
        self.init_logger()
        logger = logging.getLogger(__class__.__name__)

        # Socket is allocated for ability change the OS user
        try:
            sock = bind_socket(address=self.api_address,
                               port=self.api_port,
                               proto_name='http')
        except OSError as e:
            logger.exception(e)
            exit(1)

        if self.user is not None:
            logger.info('Changing user to %r', self.user.pw_name)
            os.setgid(self.user.pw_gid)
            os.setuid(self.user.pw_uid)

        app = create_app()
        app['storage_path'] = self.storage

        run_app(app, sock=sock)
Example #7
0
def test_bind_address(address, family, aiomisc_unused_port):
    sock = aiomisc.bind_socket(address=address, port=aiomisc_unused_port)

    assert isinstance(sock, socket.socket)
    assert sock.family == family
Example #8
0
def test_journald_logger(loop, subtests):
    with TemporaryDirectory(dir="/tmp") as tmp_dir:
        tmp_path = Path(tmp_dir)
        sock_path = tmp_path / "notify.sock"

        logs = deque()

        class FakeJournald(UDPServer):
            VALUE_LEN_STRUCT = struct.Struct("<Q")

            def handle_datagram(self, data: bytes, addr: tuple) -> None:
                result = {}
                with BytesIO(data) as fp:
                    line = fp.readline()
                    while line:
                        if b"=" not in line:
                            key = line.decode().strip()
                            value_len = self.VALUE_LEN_STRUCT.unpack(
                                fp.read(
                                    self.VALUE_LEN_STRUCT.size,
                                ),
                            )[0]
                            value = fp.read(value_len).decode()
                            assert fp.read(1) == b"\n"
                        else:
                            key, value = map(
                                lambda x: x.strip(),
                                line.decode().split("=", 1),
                            )

                        result[key] = value
                        line = fp.readline()

                logs.append(result)

        @threaded
        def log_writer():
            log = logging.getLogger("test")
            log.propagate = False
            log.handlers.clear()
            log.handlers.append(JournaldLogHandler())

            log.info("Test message")
            log.info("Test multiline\nmessage")
            log.info(
                "Test formatted: int=%d str=%s repr=%r float=%0.1f",
                1, 2, 3, 4,
            )

            log.info(
                "Message with extra", extra={
                    "foo": "bar",
                },
            )

            try:
                1 / 0
            except ZeroDivisionError:
                log.exception("Sample exception")

        with bind_socket(
            socket.AF_UNIX, socket.SOCK_DGRAM, address=str(sock_path),
        ) as sock:
            JournaldLogHandler.SOCKET_PATH = sock_path

            with aiomisc.entrypoint(FakeJournald(sock=sock), loop=loop):
                loop.run_until_complete(log_writer())

    assert len(logs) == 5

    required_fields = {
        "MESSAGE", "MESSAGE_ID", "MESSAGE_RAW", "PRIORITY",
        "SYSLOG_FACILITY", "CODE", "CODE_FUNC", "CODE_FILE",
        "CODE_LINE", "CODE_MODULE", "LOGGER_NAME", "PID",
        "PROCESS_NAME", "THREAD_ID", "THREAD_NAME",
        "RELATIVE_USEC", "CREATED_USEC",
    }

    with subtests.test("simple message"):
        message = logs.popleft()
        assert message["MESSAGE"] == "Test message"
        assert message["MESSAGE_RAW"] == "Test message"
        assert message["PRIORITY"] == "6"
        assert message["CODE_FUNC"] == "log_writer"
        assert int(message["PID"]) == os.getpid()
        for field in required_fields:
            assert field in message

    with subtests.test("multiline message"):
        message = logs.popleft()
        assert message["MESSAGE"] == "Test multiline\nmessage"
        assert message["MESSAGE_RAW"] == "Test multiline\nmessage"
        assert message["PRIORITY"] == "6"
        assert message["CODE_FUNC"] == "log_writer"
        assert int(message["PID"]) == os.getpid()

        for field in required_fields:
            assert field in message

    with subtests.test("formatted message"):
        message = logs.popleft()
        assert message["MESSAGE"] == (
            "Test formatted: int=1 str=2 repr=3 float=4.0"
        )
        assert message["MESSAGE_RAW"] == (
            "Test formatted: int=%d str=%s repr=%r float=%0.1f"
        )
        assert message["ARGUMENTS_0"] == "1"
        assert message["ARGUMENTS_1"] == "2"
        assert message["ARGUMENTS_2"] == "3"
        assert message["ARGUMENTS_3"] == "4"
        assert message["PRIORITY"] == "6"
        assert message["CODE_FUNC"] == "log_writer"
        assert int(message["PID"]) == os.getpid()

        for field in required_fields:
            assert field in message

    with subtests.test("message with extra"):
        message = logs.popleft()
        assert message["MESSAGE"] == "Message with extra"
        assert message["MESSAGE_RAW"] == "Message with extra"
        assert message["PRIORITY"] == "6"
        assert message["CODE_FUNC"] == "log_writer"
        assert message["EXTRA_FOO"] == "bar"
        assert int(message["PID"]) == os.getpid()

        for field in required_fields:
            assert field in message

    with subtests.test("exception message"):
        message = logs.popleft()
        assert message["MESSAGE"].startswith("Sample exception\nTraceback")
        assert message["MESSAGE_RAW"] == "Sample exception"
        assert message["PRIORITY"] == "3"
        assert message["CODE_FUNC"] == "log_writer"
        assert int(message["PID"]) == os.getpid()
        assert message["EXCEPTION_TYPE"] == "<class 'ZeroDivisionError'>"
        assert message["EXCEPTION_VALUE"] == "division by zero"
        assert message["TRACEBACK"].startswith(
            "Traceback (most recent call last)",
        )

        for field in required_fields:
            assert field in message