예제 #1
0
def test_services_config_parsing_errors(conf):
    """
    Test ``announcer.service.Service`` initialization - Consul config parsing errors.

    :param str conf: custom test function parameter: config file path or JSON
    """
    if conf.startswith('@'):
        with pytest.raises(AnnouncerImproperlyConfigured):
            Service('localhost', conf, ['...'])
    else:
        with pytest.raises(ValueError):
            Service('localhost', conf, ['...'])
예제 #2
0
def test_subprocess_alive(fake_consul):
    """
    Test ``announcer.service.Service`` subprocess spawning.

    :param fake_consul: custom fixture to disable calls to Consul API
    """
    service = Service('localhost', '@tests/config/correct.json',
                      ['sleep', '0.5'], None, 0.2)
    service.poll = lambda: None
    service.run()
    assert service.process.poll() is None
    time.sleep(1)
    assert service.process.poll() == 0
예제 #3
0
def test_services_config_parsing_success(fake_service):
    """
    Test ``announcer.service.Service`` initialization - Consul config parsing success.

    :param fake_service: custom fixture to disable calls to Consul API and  subprocess spawning
    """
    service = Service('localhost', '@tests/config/correct.json', ['...'])
    assert len(service.services) == 3
    assert len(service.ttl_checks) == 1

    service = Service(
        'localhost', '{"service": {"name": "simple service", "check": {"ttl": "8s"}}}', ['...']
    )
    assert len(service.services) == 1
    assert len(service.ttl_checks) == 1
예제 #4
0
def test_consul_interaction():
    """
    Test ``announcer.service.Service`` interaction with Consul.
    """
    api_url = 'http://*****:*****@tests/config/correct.json', ['sleep', '0.2'],
            None, 0.1).run()
예제 #5
0
def test_interval_parsing(fake_service, caplog):
    """
    Test ``announcer.service.Service`` initialization - interval parsing.
    """
    # Interval is provided
    assert Service('localhost', '@tests/config/correct.json', ['...'], None, 3).interval == 3

    # Default interval is 1 sec
    assert Service('localhost', '@tests/config/correct.json', ['...']).interval == 1

    # Interval is auto-calculated as min TTL / 10
    assert Service('localhost', '@tests/config/correct.json', ['...'], None, None).interval == 1.5

    # No TTL specified
    with pytest.raises(AnnouncerImproperlyConfigured):
        Service('localhost', '@tests/config/correct-no-ttl.json', ['...'], None, None)

    Service('localhost', '@tests/config/correct.json', ['...'], None, 20.0)
    log_record = caplog.records[-1]
    assert log_record.levelname == 'WARNING'
    assert log_record.message == 'Polling interval (20.0 sec) is greater than min TTL (15.0 sec)'
예제 #6
0
def test_subprocess_cleanup(fake_consul):
    """
    Test ``announcer.service.Service`` subprocess termination when Python process has exited.

    :param fake_consul: custom fixture to disable calls to Consul API
    """
    service = Service('localhost', '@tests/config/correct.json',
                      ['tail', '-f', '/dev/null'])
    service.poll = lambda: None
    service.run()
    service.__del__()  # this method is called by Python on garbage collection
    time.sleep(0.5)  # we need to wait some time until the process is killed
    assert service.process.poll() is not None  # subprocess was killed
예제 #7
0
def test_subprocess_polling(fake_consul, caplog, monkeypatch):
    """
    Test ``announcer.service.Service`` subprocess keeping alive.

    :param fake_consul: custom fixture to disable calls to Consul API
    :param caplog: ``pytest-catchlog`` fixture to catch Python logs
    :param monkeypatch: pytest "patching" fixture
    """
    monkeypatch.setattr(root_logger, 'level', logging.DEBUG)
    service = Service('localhost', '@tests/config/correct.json',
                      ['sleep', '0.2'], None, 0.1)
    service.run()
    assert service.process.poll() == 0

    # No TTL checks - log a message
    service = Service('localhost', '@tests/config/correct-no-ttl.json',
                      ['sleep', '0.2'], None, 0.1)
    service.run()
    assert service.process.poll() == 0
    assert caplog.records[-1].message == "No TTL checks registered"
예제 #8
0
def main():
    parser = argparse.ArgumentParser(
        'consul-announcer',
        description="Service announcer for Consul.",
        formatter_class=ArgsFormatter)

    parser.add_argument(
        '--agent',
        default=os.getenv('CONSUL_ANNOUNCER_AGENT', 'localhost'),
        help="Consul agent address: hostname[:port]. "
        "Default: localhost (default port is 8500). "
        "You can also use CONSUL_ANNOUNCER_AGENT env variable.",
        metavar='hostname[:port]')

    parser.add_argument(
        '--config',
        required='CONSUL_ANNOUNCER_CONFIG' not in os.environ,
        default=os.getenv('CONSUL_ANNOUNCER_CONFIG'),
        help="Consul configuration JSON (required). "
        "If starts with @ - considered as file path. "
        "You can also use CONSUL_ANNOUNCER_CONFIG env variable.",
        metavar='"JSON or @path"')

    parser.add_argument(
        '--token',
        default=os.getenv('CONSUL_ANNOUNCER_TOKEN'),
        help="Consul ACL token. "
        "You can also use CONSUL_ANNOUNCER_TOKEN env variable.",
        metavar='acl-token')

    parser.add_argument(
        '--interval',
        default=os.getenv('CONSUL_ANNOUNCER_INTERVAL'),
        help=
        "interval for periodic marking all TTL checks as passed, in seconds. "
        "Should be less than min TTL. "
        "You can also use CONSUL_ANNOUNCER_INTERVAL env variable.",
        metavar='seconds',
        type=float)

    parser.add_argument('--verbose',
                        '-v',
                        action='count',
                        help="verbose output. You can specify -v or -vv")

    if '--' not in sys.argv:
        if "--help" in sys.argv or "-h" in sys.argv or len(sys.argv) == 1:
            parser.print_help()
            sys.exit()
        else:
            parser.print_usage()
            sys.stderr.write("{}: error: command is not specified".format(
                parser.prog))
            sys.exit(2)

    split_at = sys.argv.index('--')
    args = parser.parse_args(sys.argv[1:split_at])
    cmd = sys.argv[split_at + 1:]

    if not args.verbose:
        root_logger.setLevel(logging.WARNING)
    elif args.verbose == 1:
        root_logger.setLevel(logging.INFO)
    elif args.verbose >= 2:
        root_logger.setLevel(logging.DEBUG)

    try:
        Service(agent_address=args.agent,
                config=args.config,
                cmd=cmd,
                token=args.token,
                interval=args.interval).run()
    except ConnectionError as e:
        logger.error("Can't connect to \"{}\"".format(e.request.url))
        sys.exit(1)
    except (AnnouncerImproperlyConfigured, OSError, ValueError) as e:
        logger.error(e)
        sys.exit(1)