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
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'))
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()
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()
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()