Example #1
0
    def test_bootstrap(self):
        """
        Verify Transport().bootstrap works as expected.
        """
        with patch(
                'commissaire.transport.ansibleapi.TaskQueueManager') as _tqm:
            _tqm().run.return_value = 0

            transport = ansibleapi.Transport()
            transport.variable_manager._fact_cache = {}
            oscmd = MagicMock(OSCmdBase)

            config = Config(etcd={
                'uri': urlparse('http://127.0.0.1:2379'),
            },
                            kubernetes={
                                'uri': urlparse('http://127.0.0.1:8080'),
                                'token': 'token',
                            })

            result, facts = transport.bootstrap('10.2.0.2', 'test/fake_key',
                                                config, oscmd)
            # We should have a successful response
            self.assertEquals(0, result)
            # We should see expected calls
            self.assertEquals(1, oscmd.install_docker.call_count)
            self.assertEquals(1, oscmd.install_kube.call_count)
    def test_bootstrap(self):
        """
        Verify Transport().bootstrap works as expected.
        """
        with patch(
                'commissaire.transport.ansibleapi.TaskQueueManager') as _tqm:
            _tqm().run.return_value = 0

            transport = ansibleapi.Transport()
            transport.variable_manager._fact_cache = {}
            oscmd = MagicMock(OSCmdBase)

            config = Config(etcd={
                'uri': urlparse('http://127.0.0.1:2379'),
            },
                            kubernetes={
                                'uri': urlparse('http://127.0.0.1:8080'),
                                'token': 'token',
                            })

            result, facts = transport.bootstrap('10.2.0.2', 'test/fake_key',
                                                config, oscmd)
            # We should have a successful response
            self.assertEquals(0, result)
            # We should see expected calls
            self.assertEquals(1, oscmd.install_docker.call_count)
            self.assertEquals(1, oscmd.install_kube.call_count)

            # Check 'commissaire_enable_pkg_repos' playbook variable
            # for various operating systems.
            transport = ansibleapi.Transport()
            transport._run = MagicMock()
            transport._run.return_value = (0, {})

            needs_enable_repos = ('redhat', 'rhel')

            for os_type in available_os_types:
                oscmd = get_oscmd(os_type)
                result, facts = transport.bootstrap('10.2.0.2.',
                                                    'test/fake_key', config,
                                                    oscmd)
                play_vars = transport._run.call_args[0][4]
                command = play_vars['commissaire_enable_pkg_repos']
                if os_type in needs_enable_repos:
                    self.assertIn('subscription-manager repos', command)
                else:
                    self.assertEqual('true', command)  # no-op command
Example #3
0
    def test_node_registered(self):
        """
        Verify that KuberContainerManager().node_registered() works as expected.
        """
        config = Config(
            etcd={
                'uri': urlparse('http://127.0.0.1:2379'),
            },
            kubernetes={
                'uri': urlparse('http://127.0.0.1:8080'),
                'token': 'token',
            }
        )
        kube_container_mgr = KubeContainerManager(config)
        # First call should return True. The rest should be False.
        kube_container_mgr.con = MagicMock()
        kube_container_mgr.con.get = MagicMock(side_effect=(
            MagicMock(status_code=200),
            MagicMock(status_code=404),
            MagicMock(status_code=500)))

        self.assertTrue(kube_container_mgr.node_registered('test'))
        self.assertFalse(kube_container_mgr.node_registered('test'))
        self.assertFalse(kube_container_mgr.node_registered('test'))
Example #4
0
def main():  # pragma: no cover
    """
    Main script entry point.
    """
    from commissaire.cherrypy_plugins.store import StorePlugin
    from commissaire.cherrypy_plugins.investigator import InvestigatorPlugin
    config = Config()

    epilog = ('Example: ./commissaire -e http://127.0.0.1:2379'
              ' -k http://127.0.0.1:8080')

    parser = argparse.ArgumentParser(epilog=epilog)

    try:
        args = parse_args(parser)
        config.etcd['uri'] = parse_uri(args.etcd_uri, 'etcd')
        config.kubernetes['uri'] = parse_uri(args.kube_uri, 'kube')
    except Exception:
        _, ex, _ = exception.raise_if_not(Exception)
        parser.error(ex)

    store_kwargs = {
        'read_timeout': 2,
        'host': config.etcd['uri'].hostname,
        'port': config.etcd['uri'].port,
        'protocol': config.etcd['uri'].scheme,
    }

    if bool(args.etcd_ca_path):
        config.etcd['certificate_ca_path'] = args.etcd_ca_path

    # We need all args to use a client side cert for etcd
    if bool(args.etcd_cert_path) ^ bool(args.etcd_cert_key_path):
        parser.error(
            'Both etcd-cert-path and etcd-cert-key-path must be '
            'provided to use a client side certificate with etcd.')
    elif bool(args.etcd_cert_path):
        if config.etcd['uri'].scheme != 'https':
            parser.error('An https URI is required when using '
                         'client side certificates.')
        store_kwargs['cert'] = (args.etcd_cert_path, args.etcd_cert_key_path)
        config.etcd['certificate_path'] = args.etcd_cert_path
        config.etcd['certificate_key_path'] = args.etcd_cert_key_path
        logging.info('Using client side certificate for etcd.')

    ds = etcd.Client(**store_kwargs)

    try:
        logging.config.dictConfig(
            json.loads(ds.read('/commissaire/config/logger').value))
        logging.info('Using Etcd for logging configuration.')
    except etcd.EtcdKeyNotFound:
        found_logger_config = False
        for logger_path in (
                '/etc/commissaire/logger.json', './conf/logger.json'):
            if os.path.isfile(logger_path):
                with open(logger_path, 'r') as logging_default_cfg:
                    logging.config.dictConfig(
                        json.loads(logging_default_cfg.read()))
                    found_logger_config = True
                logging.warn('No logger configuration in Etcd. Using defaults '
                             'at {0}'.format(logger_path))
        if not found_logger_config:
            parser.error(
                'Unable to find any logging configuration. Exiting ...')

    except etcd.EtcdConnectionFailed:
        _, ecf, _ = exception.raise_if_not(etcd.EtcdConnectionFailed)
        err = 'Unable to connect to Etcd: {0}. Exiting ...'.format(ecf)
        logging.fatal(err)
        parser.error('{0}\n'.format(err))
        raise SystemExit(1)

    # TLS options
    tls_keyfile = cli_etcd_or_default(
        'tlskeyfile', args.tls_keyfile, None, ds)
    tls_certfile = cli_etcd_or_default(
        'tlscertfile', args.tls_certfile, None, ds)
    tls_clientverifyfile = cli_etcd_or_default(
        'tls-clientverifyfile', args.tls_clientverifyfile, None, ds)

    interface = cli_etcd_or_default(
        'listeninterface', args.listen_interface, '0.0.0.0', ds)
    port = cli_etcd_or_default('listenport', args.listen_port, 8000, ds)

    # Pull options for accessing kubernetes
    try:
        config.kubernetes['token'] = ds.get(
            '/commissaire/config/kubetoken').value
        logging.info('Using kubetoken for kubernetes.')
    except etcd.EtcdKeyNotFound:
        logging.debug('No kubetoken set.')
    try:
        config.kubernetes['certificate_path'] = ds.get(
            '/commissaire/config/kube_certificate_path').value
        config.kubernetes['certificate_key_path'] = ds.get(
            '/commissaire/config/kube_certificate_key_path').value
        logging.info('Using client side certificate for kubernetes.')
    except etcd.EtcdKeyNotFound:
        logging.debug('No kubernetes client side certificate set.')

    # Add our config instance to the cherrypy global config so we can use it's
    # values elsewhere
    # TODO: Technically this should be in the cherrypy.request.app.config
    # but it looks like that isn't accessable with WSGI based apps
    cherrypy.config['commissaire.config'] = config

    logging.debug('Config: {0}'.format(config))

    cherrypy.server.unsubscribe()
    # Disable autoreloading and use our logger
    cherrypy.config.update({'log.screen': False,
                            'log.access_file': '',
                            'log.error_file': '',
                            'engine.autoreload.on': False})

    new_ssl_adapter_cls = type(
        "CustomClientCertBuiltinSSLAdapter",
        (ClientCertBuiltinSSLAdapter,),
        {"verify_location": tls_clientverifyfile}
    )

    if sys.version_info < (3, 0):
        from cherrypy.wsgiserver.wsgiserver2 import ssl_adapters
    else:
        from cherrypy.wsgiserver.wsgiserver3 import ssl_adapters
    ssl_adapters['builtin_client'] = new_ssl_adapter_cls

    server = cherrypy._cpserver.Server()
    server.socket_host = interface
    server.socket_port = int(port)
    server.thread_pool = 10

    if bool(tls_keyfile) ^ bool(tls_certfile):
        parser.error(
            'Both a keyfile and certfile must be '
            'given for commissaire server TLS. Exiting ...')
    elif bool(tls_clientverifyfile) and not bool(tls_certfile):
        parser.error(
            'If a client verify file is given a TLS keyfile and '
            'certfile must be given as well. Exiting ...')

    if tls_keyfile and tls_certfile:
        server.ssl_module = 'builtin_client'
        server.ssl_certificate = tls_certfile
        server.ssl_private_key = tls_keyfile
        logging.info('Commissaire server TLS will be enabled.')
    server.subscribe()

    # Handle UNIX signals (SIGTERM, SIGHUP, SIGUSR1)
    if hasattr(cherrypy.engine, 'signal_handler'):
        cherrypy.engine.signal_handler.subscribe()

    # Add our plugins
    InvestigatorPlugin(cherrypy.engine, config).subscribe()

    # Configure the store plugin before starting it.
    store_plugin = StorePlugin(cherrypy.engine)
    store_manager = store_plugin.get_store_manager()

    # XXX Temporary until we have a real storage plugin system.
    store_manager.register_store_handler(
        EtcdStoreHandler, store_kwargs, BogusModelType)

    store_plugin.subscribe()

    # NOTE: Anything that requires etcd should start AFTER
    # the engine is started
    cherrypy.engine.start()

    try:
        # Make and mount the app
        authentication_kwargs = {}
        if type(args.authentication_plugin_kwargs) is str:
            if '=' in args.authentication_plugin_kwargs:
                for item in args.authentication_plugin_kwargs.split(','):
                    key, value = item.split('=')
                    authentication_kwargs[key.strip()] = value.strip()
        elif type(args.authentication_plugin_kwargs) is dict:
            # _read_config_file() sets this up.
            authentication_kwargs = args.authentication_plugin_kwargs

        app = create_app(
            args.authentication_plugin,
            authentication_kwargs)
        cherrypy.tree.graft(app, "/")

        # Serve forever
        cherrypy.engine.block()
    except Exception:
        _, ex, _ = exception.raise_if_not(Exception)
        logging.fatal('Unable to start server: {0}'.format(ex))
        cherrypy.engine.stop()
Example #5
0
def main():  # pragma: no cover
    """
    Main script entry point.
    """
    import argparse
    from commissaire.config import Config

    config = Config()

    epilog = ('Example: ./commissaire -e http://127.0.0.1:2379'
              ' -k http://127.0.0.1:8080')

    parser = argparse.ArgumentParser(epilog=epilog)
    parser.add_argument(
        '--listen-interface', '-i', type=str, help='Interface to listen on')
    parser.add_argument(
        '--listen-port', '-p', type=int, help='Port to listen on')
    parser.add_argument(
        '--etcd-uri', '-e', type=str, required=True,
        help='Full URI for etcd EX: http://127.0.0.1:2379')
    parser.add_argument(
        '--kube-uri', '-k', type=str, required=True,
        help='Full URI for kubernetes EX: http://127.0.0.1:8080')
    args = parser.parse_args()

    try:
        config.etcd['uri'] = parse_uri(args.etcd_uri, 'etcd')
        config.kubernetes['uri'] = parse_uri(args.kube_uri, 'kube')
    except Exception:
        _, ex, _ = exception.raise_if_not(Exception)
        parser.error(ex)

    ds = etcd.Client(
        host=config.etcd['uri'].hostname, port=config.etcd['uri'].port)

    try:
        logging.config.dictConfig(
            json.loads(ds.get('/commissaire/config/logger').value))
        logging.info('Using Etcd for logging configuration.')
    except etcd.EtcdKeyNotFound:
        with open('./conf/logger.json', 'r') as logging_default_cfg:
            logging.config.dictConfig(json.loads(logging_default_cfg.read()))
            logging.warn('No logger configuration in Etcd. Using defaults.')
    except etcd.EtcdConnectionFailed:
        _, ecf, _ = exception.raise_if_not(etcd.EtcdConnectionFailed)
        err = 'Unable to connect to Etcd: {0}. Exiting ...'.format(ecf)
        logging.fatal(err)
        parser.error('{0}\n'.format(err))
        raise SystemExit(1)

    interface = cli_etcd_or_default(
        'listeninterface', args.listen_interface, '0.0.0.0', ds)
    port = cli_etcd_or_default('listenport', args.listen_port, 8000, ds)
    config.etcd['listen'] = urlparse('http://{0}:{1}'.format(
        interface, port))

    try:
        config.kubernetes['token'] = ds.get(
            '/commissaire/config/kubetoken').value
        logging.debug('Config: {0}'.format(config))
        POOLS['investigator'].spawn(
            investigator, INVESTIGATE_QUEUE, config, ds)
    except etcd.EtcdKeyNotFound:
        parser.error('"/commissaire/config/kubetoken" must be set in etcd!')
    # watch_thread = gevent.spawn(host_watcher, ROUTER_QUEUE, ds)
    # router_thread = gevent.spawn(router, ROUTER_QUEUE)

    app = create_app(ds)
    try:
        WSGIServer((interface, int(port)), app).serve_forever()
    except KeyboardInterrupt:
        pass

    POOLS['investigator'].kill()
Example #6
0
def main():  # pragma: no cover
    """
    Main script entry point.
    """
    import argparse

    from multiprocessing import Process
    from commissaire.cherrypy_plugins import CherryPyStorePlugin
    config = Config()

    epilog = ('Example: ./commissaire -e http://127.0.0.1:2379'
              ' -k http://127.0.0.1:8080')

    parser = argparse.ArgumentParser(epilog=epilog)
    parser.add_argument(
        '--listen-interface', '-i', type=str, help='Interface to listen on')
    parser.add_argument(
        '--listen-port', '-p', type=int, help='Port to listen on')
    parser.add_argument(
        '--etcd-uri', '-e', type=str, required=True,
        help='Full URI for etcd EX: http://127.0.0.1:2379')
    parser.add_argument(
        '--etcd-cert-path', '-C', type=str, required=False,
        help='Full path to the client side certificate.')
    parser.add_argument(
        '--etcd-cert-key-path', '-K', type=str, required=False,
        help='Full path to the client side certificate key.')
    parser.add_argument(
        '--etcd-ca-path', '-A', type=str, required=False,
        help='Full path to the CA file.')
    parser.add_argument(
        '--kube-uri', '-k', type=str, required=True,
        help='Full URI for kubernetes EX: http://127.0.0.1:8080')
    parser.add_argument(
        '--tls-keyfile', type=str, required=False,
        help='Full path to the TLS keyfile for the commissaire server')
    parser.add_argument(
        '--tls-certfile', type=str, required=False,
        help='Full path to the TLS certfile for the commissaire server')
    parser.add_argument(
        '--authentication-plugin', type=str, required=False,
        default='commissaire.authentication.httpauthbyetcd',
        help=('Authentication Plugin module. '
              'EX: commissaire.authentication.httpauth'))
    parser.add_argument(
        '--authentication-plugin-kwargs', type=str,
        required=False, default={},
        help='Authentication Plugin configuration (key=value)')

    args = parser.parse_args()

    try:
        config.etcd['uri'] = parse_uri(args.etcd_uri, 'etcd')
        config.kubernetes['uri'] = parse_uri(args.kube_uri, 'kube')
    except Exception:
        _, ex, _ = exception.raise_if_not(Exception)
        parser.error(ex)

    store_kwargs = {
        'read_timeout': 2,
        'host': config.etcd['uri'].hostname,
        'port': config.etcd['uri'].port,
        'protocol': config.etcd['uri'].scheme,
    }

    if bool(args.etcd_ca_path):
        config.etcd['certificate_ca_path'] = args.etcd_ca_path

    # We need all args to use a client side cert for etcd
    if bool(args.etcd_cert_path) ^ bool(args.etcd_cert_key_path):
        parser.error(
            'Both etcd-cert-path and etcd-cert-key-path must be '
            'provided to use a client side certificate with etcd.')
    elif bool(args.etcd_cert_path):
        if config.etcd['uri'].scheme != 'https':
            parser.error('An https URI is required when using '
                         'client side certificates.')
        store_kwargs['cert'] = (args.etcd_cert_path, args.etcd_cert_key_path)
        config.etcd['certificate_path'] = args.etcd_cert_path
        config.etcd['certificate_key_path'] = args.etcd_cert_key_path
        logging.info('Using client side certificate for etcd.')

    ds = etcd.Client(**store_kwargs)

    try:
        logging.config.dictConfig(
            json.loads(ds.read('/commissaire/config/logger').value))
        logging.info('Using Etcd for logging configuration.')
    except etcd.EtcdKeyNotFound:
        found_logger_config = False
        for logger_path in (
                '/etc/commissaire/logger.json', './conf/logger.json'):
            if os.path.isfile(logger_path):
                with open(logger_path, 'r') as logging_default_cfg:
                    logging.config.dictConfig(
                        json.loads(logging_default_cfg.read()))
                    found_logger_config = True
                logging.warn('No logger configuration in Etcd. Using defaults '
                             'at {0}'.format(logger_path))
        if not found_logger_config:
            parser.error(
                'Unable to find any logging configuration. Exiting ...')
    except etcd.EtcdConnectionFailed:
        _, ecf, _ = exception.raise_if_not(etcd.EtcdConnectionFailed)
        err = 'Unable to connect to Etcd: {0}. Exiting ...'.format(ecf)
        logging.fatal(err)
        parser.error('{0}\n'.format(err))
        raise SystemExit(1)

    # TLS options
    tls_keyfile = cli_etcd_or_default(
        'tlskeyfile', args.tls_keyfile, None, ds)
    tls_certfile = cli_etcd_or_default(
        'tlscertfile', args.tls_certfile, None, ds)

    interface = cli_etcd_or_default(
        'listeninterface', args.listen_interface, '0.0.0.0', ds)
    port = cli_etcd_or_default('listenport', args.listen_port, 8000, ds)

    # Pull options for accessing kubernetes
    try:
        config.kubernetes['token'] = ds.get(
            '/commissaire/config/kubetoken').value
        logging.info('Using kubetoken for kubernetes.')
    except etcd.EtcdKeyNotFound:
        logging.debug('No kubetoken set.')
    try:
        config.kubernetes['certificate_path'] = ds.get(
            '/commissaire/config/kube_certificate_path').value
        config.kubernetes['certificate_key_path'] = ds.get(
            '/commissaire/config/kube_certificate_key_path').value
        logging.info('Using client side certificate for kubernetes.')
    except etcd.EtcdKeyNotFound:
        logging.debug('No kubernetes client side certificate set.')

    # Add our config instance to the cherrypy global config so we can use it's
    # values elsewhere
    # TODO: Technically this should be in the cherrypy.request.app.config
    # but it looks like that isn't accessable with WSGI based apps
    cherrypy.config['commissaire.config'] = config

    logging.debug('Config: {0}'.format(config))

    cherrypy.server.unsubscribe()
    # Disable autoreloading and use our logger
    cherrypy.config.update({'log.screen': False,
                            'log.access_file': '',
                            'log.error_file': '',
                            'engine.autoreload.on': False})

    server = cherrypy._cpserver.Server()
    server.socket_host = interface
    server.socket_port = int(port)
    server.thread_pool = 10

    if bool(tls_keyfile) ^ bool(tls_certfile):
        parser.error(
            'Both a keyfile and certfile must be '
            'given for commissaire server TLS. Exiting ...')
    if tls_keyfile and tls_certfile:
        server.ssl_module = 'builtin'
        server.ssl_certificate = tls_certfile
        server.ssl_private_key = tls_keyfile
        logging.info('Commissaire server TLS will be enabled.')
    server.subscribe()

    # Add our plugins
    CherryPyStorePlugin(cherrypy.engine, store_kwargs).subscribe()
    # NOTE: Anything that requires etcd should start AFTER
    # the engine is started
    cherrypy.engine.start()

    # Start processes
    PROCS['investigator'] = Process(
        target=investigator, args=(INVESTIGATE_QUEUE, config))
    PROCS['investigator'].start()

    try:
        # Make and mount the app
        authentication_kwargs = {}
        if '=' in args.authentication_plugin_kwargs:
            for item in args.authentication_plugin_kwargs.split(','):
                key, value = item.split('=')
                authentication_kwargs[key.strip()] = value.strip()
        app = create_app(
            ds,
            args.authentication_plugin,
            authentication_kwargs)
        cherrypy.tree.graft(app, "/")

        # Serve forever
        cherrypy.engine.block()
    except Exception:
        _, ex, _ = exception.raise_if_not(Exception)
        logging.fatal('Unable to start server: {0}'.format(ex))
        cherrypy.engine.stop()

    PROCS['investigator'].terminate()
    PROCS['investigator'].join()