diff --git a/gordo_components/cli.py b/gordo_components/cli.py index ac7520317..bb4766019 100644 --- a/gordo_components/cli.py +++ b/gordo_components/cli.py @@ -5,6 +5,7 @@ ''' import logging +import ast import os from ast import literal_eval @@ -82,9 +83,13 @@ def run_server_cli(host, port): @click.command('run-watchman') -@click.option('--host', type=str, help='The host to run the server on.') -@click.option('--port', type=int, help='The port to run the server on.') -def run_watchman_cli(host, port): +@click.argument('project-name', envvar='PROJECT_NAME', type=str) +@click.argument('target-names', envvar='TARGET_NAMES', type=ast.literal_eval) +@click.argument('target-names-sanitized', envvar='TARGET_NAMES_SANITIZED', type=ast.literal_eval) +@click.option('--host', type=str, help='The host to run the server on.', default='0.0.0.0') +@click.option('--port', type=int, help='The port to run the server on.', default=5555) +@click.option('--debug', type=bool, help='Run in debug mode', default=False) +def run_watchman_cli(project_name, target_names, target_names_sanitized, host, port, debug): """ Start the Gordo Watchman server for this project. Which is responsible for dynamically comparing expected URLs derived from a project config fle @@ -96,7 +101,7 @@ def run_watchman_cli(host, port): TARGET_NAMES: A list of non-sanitized machine / target names TARGET_NAMES_SANITIZED: Same list of names, only sanitized """ - watchman.server.run_server(host, port) + watchman.server.run_server(host, port, debug, project_name, target_names, target_names_sanitized) gordo.add_command(build) diff --git a/gordo_components/watchman/server.py b/gordo_components/watchman/server.py index 8537fd315..e0d392805 100644 --- a/gordo_components/watchman/server.py +++ b/gordo_components/watchman/server.py @@ -1,10 +1,8 @@ # -*- coding: utf-8 -*- -import os -import yaml -import ast import requests import logging +from typing import Iterable from flask import Flask, jsonify, make_response from flask.views import MethodView from concurrent.futures import ThreadPoolExecutor @@ -15,6 +13,8 @@ # Will contain a list of endpoints to expected models via Ambassador # see _load_endpoints() ENDPOINTS = None +PROJECT_NAME = None +TARGET_NAMES = None logger = logging.getLogger(__name__) @@ -41,7 +41,7 @@ def get(self): # List of dicts: [{'endpoint': /path/to/endpoint, 'healthy': bool}] results = [{'endpoint': futures[f], 'healthy': f.result()} for f in futures] - payload = jsonify({'endpoints': results, 'project_name': os.environ['PROJECT_NAME']}) + payload = jsonify({'endpoints': results, 'project_name': PROJECT_NAME}) resp = make_response(payload, 200) resp.headers['Cache-Control'] = 'max-age=0' return resp @@ -51,48 +51,35 @@ def healthcheck(): """ Return gordo version, route for Watchman server """ - payload = jsonify({'version': __version__, 'config': yaml.load(os.environ['TARGET_NAMES'])}) + payload = jsonify({'version': __version__, 'config': TARGET_NAMES}) return payload, 200 -def build_app(): +def build_app(project_name: str, target_names: Iterable[str], target_names_sanitized: Iterable[str]): """ Build app and any associated routes """ - global ENDPOINTS - ENDPOINTS = _load_endpoints() + # Precompute list of expected endpoints from config file and other global env + global ENDPOINTS, PROJECT_NAME, TARGET_NAMES + ENDPOINTS = [f'/gordo/v0/{project_name}/{sanitized_name}/healthcheck' + for sanitized_name in target_names_sanitized] + PROJECT_NAME = project_name + TARGET_NAMES = target_names + + # App and routes app = Flask(__name__) app.add_url_rule(rule='/healthcheck', view_func=healthcheck, methods=['GET']) app.add_url_rule(rule='/', view_func=WatchmanApi.as_view('sentinel_api'), methods=['GET']) return app -def run_server(host: str = '0.0.0.0', port: int = 5555, debug: bool = False): - app = build_app() - app.run(host, port, debug=debug) - - -def _load_endpoints(): - """ - Given the current environment vars of TARGET_NAMES, PROJECT_NAME, AMBASSADORHOST and PORT: build a list - of pre-computed expected endpoints - """ - if 'TARGET_NAMES_SANITIZED' not in os.environ or 'TARGET_NAMES' not in os.environ: - raise EnvironmentError('Need to have TARGET_NAMES_SANITIZED and TARGET_NAMES environment variables set as a' - ' list of expected, sanitized and non-sanitized target / machine names.') - if 'PROJECT_NAME' not in os.environ: - raise EnvironmentError('Need to have PROJECT_NAME environment variable set.') - - TARGET_NAMES_SANITIZED = ast.literal_eval(os.environ['TARGET_NAMES_SANITIZED']) - _TARGET_NAMES = ast.literal_eval(os.environ['TARGET_NAMES']) - project_name = os.environ["PROJECT_NAME"] - - # Precompute list of expected endpoints from config file - endpoints = [f'/gordo/v0/{project_name}/{sanitized_name}/healthcheck' - for sanitized_name in TARGET_NAMES_SANITIZED] - return endpoints +def run_server(host: str, + port: int, + debug: bool, + project_name: str, + target_names: Iterable[str], + target_names_sanitized: Iterable[str]): - -if __name__ == '__main__': - run_server() + app = build_app(project_name, target_names, target_names_sanitized) + app.run(host, port, debug=debug) diff --git a/tests/test_watchman.py b/tests/test_watchman.py index 07753f3a6..15f86e594 100644 --- a/tests/test_watchman.py +++ b/tests/test_watchman.py @@ -6,13 +6,10 @@ from gordo_components import __version__ from gordo_components.watchman import server -from tests.utils import temp_env_vars TARGET_NAMES = ['CT-machine-name-456', 'CT-machine-name-123'] -TARGET_NAMES_STR = str(TARGET_NAMES) TARGET_NAMES_SANITIZED = ['ct-machine-name-456-kn209d', 'ct-machine-name-123-ksno0s9f092'] -TARGET_NAMES_SANITIZED_STR = str(TARGET_NAMES_SANITIZED) PROJECT_NAME = 'some-project-name' AMBASSADORHOST = 'ambassador' URL_FORMAT = 'http://{host}/gordo/v0/{project_name}/{sanitized_name}/healthcheck' @@ -30,20 +27,17 @@ def request_callback(_request): class WatchmanTestCase(unittest.TestCase): - @temp_env_vars(TARGET_NAMES=TARGET_NAMES_STR, TARGET_NAMES_SANITIZED=TARGET_NAMES_SANITIZED_STR, PROJECT_NAME=PROJECT_NAME) def setUp(self): - app = server.build_app() + app = server.build_app(project_name=PROJECT_NAME, target_names=TARGET_NAMES, target_names_sanitized=TARGET_NAMES_SANITIZED) app.testing = True self.app = app.test_client() - @temp_env_vars(TARGET_NAMES=TARGET_NAMES_STR, TARGET_NAMES_SANITIZED=TARGET_NAMES_SANITIZED_STR, PROJECT_NAME=PROJECT_NAME) def test_healthcheck(self): resp = self.app.get('/healthcheck') self.assertEqual(resp.status_code, 200) resp = resp.get_json() self.assertTrue('version' in resp) - @temp_env_vars(TARGET_NAMES=TARGET_NAMES_STR, TARGET_NAMES_SANITIZED=TARGET_NAMES_SANITIZED_STR, PROJECT_NAME=PROJECT_NAME) @responses.activate def test_api(self): """