|  |  | @ -16,15 +16,11 @@ from flask import ( | 
			
		
	
		
		
			
				
					
					|  |  |  |     Flask, |  |  |  |     Flask, | 
			
		
	
		
		
			
				
					
					|  |  |  |     Response, |  |  |  |     Response, | 
			
		
	
		
		
			
				
					
					|  |  |  |     abort, |  |  |  |     abort, | 
			
		
	
		
		
			
				
					
					|  |  |  |     redirect, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     render_template, |  |  |  |     render_template, | 
			
		
	
		
		
			
				
					
					|  |  |  |     request, |  |  |  |     request, | 
			
		
	
		
		
			
				
					
					|  |  |  |     send_from_directory, |  |  |  |     send_from_directory, | 
			
		
	
		
		
			
				
					
					|  |  |  |     session, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | ) |  |  |  | ) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | from flask_oauthlib.client import OAuth |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | from functools import wraps |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | from gevent import sleep, spawn |  |  |  | from gevent import sleep, spawn | 
			
		
	
		
		
			
				
					
					|  |  |  | from gevent.pywsgi import WSGIServer |  |  |  | from gevent.pywsgi import WSGIServer | 
			
		
	
		
		
			
				
					
					|  |  |  | from jq import jq |  |  |  | from jq import jq | 
			
		
	
	
		
		
			
				
					|  |  | @ -34,11 +30,9 @@ from os import getenv | 
			
		
	
		
		
			
				
					
					|  |  |  | from re import X, compile |  |  |  | from re import X, compile | 
			
		
	
		
		
			
				
					
					|  |  |  | from requests.exceptions import RequestException |  |  |  | from requests.exceptions import RequestException | 
			
		
	
		
		
			
				
					
					|  |  |  | from signal import SIGTERM, signal |  |  |  | from signal import SIGTERM, signal | 
			
		
	
		
		
			
				
					
					|  |  |  | from urllib.parse import urljoin |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | from . import __version__ |  |  |  | from . import __version__ | 
			
		
	
		
		
			
				
					
					|  |  |  | from .cluster_discovery import DEFAULT_CLUSTERS, StaticClusterDiscoverer |  |  |  | from .cluster_discovery import DEFAULT_CLUSTERS, StaticClusterDiscoverer | 
			
		
	
		
		
			
				
					
					|  |  |  | from .oauth import OAuthRemoteAppWithRefresh |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | from .spiloutils import ( |  |  |  | from .spiloutils import ( | 
			
		
	
		
		
			
				
					
					|  |  |  |     apply_postgresql, |  |  |  |     apply_postgresql, | 
			
		
	
	
		
		
			
				
					|  |  | @ -71,11 +65,8 @@ logger = getLogger(__name__) | 
			
		
	
		
		
			
				
					
					|  |  |  | SERVER_STATUS = {'shutdown': False} |  |  |  | SERVER_STATUS = {'shutdown': False} | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | APP_URL = getenv('APP_URL') |  |  |  | APP_URL = getenv('APP_URL') | 
			
		
	
		
		
			
				
					
					|  |  |  | AUTHORIZE_URL = getenv('AUTHORIZE_URL') |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | SPILO_S3_BACKUP_BUCKET = getenv('SPILO_S3_BACKUP_BUCKET') |  |  |  | SPILO_S3_BACKUP_BUCKET = getenv('SPILO_S3_BACKUP_BUCKET') | 
			
		
	
		
		
			
				
					
					|  |  |  | TEAM_SERVICE_URL = getenv('TEAM_SERVICE_URL') |  |  |  | TEAM_SERVICE_URL = getenv('TEAM_SERVICE_URL') | 
			
		
	
		
		
			
				
					
					|  |  |  | ACCESS_TOKEN_URL = getenv('ACCESS_TOKEN_URL') |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | TOKENINFO_URL = getenv('OAUTH2_TOKEN_INFO_URL') |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | OPERATOR_API_URL = getenv('OPERATOR_API_URL', 'http://postgres-operator') |  |  |  | OPERATOR_API_URL = getenv('OPERATOR_API_URL', 'http://postgres-operator') | 
			
		
	
		
		
			
				
					
					|  |  |  | OPERATOR_CLUSTER_NAME_LABEL = getenv('OPERATOR_CLUSTER_NAME_LABEL', 'cluster-name') |  |  |  | OPERATOR_CLUSTER_NAME_LABEL = getenv('OPERATOR_CLUSTER_NAME_LABEL', 'cluster-name') | 
			
		
	
	
		
		
			
				
					|  |  | @ -184,38 +175,6 @@ class WSGITransferEncodingChunked: | 
			
		
	
		
		
			
				
					
					|  |  |  |         return self.app(environ, start_response) |  |  |  |         return self.app(environ, start_response) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | oauth = OAuth(app) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | auth = OAuthRemoteAppWithRefresh( |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     oauth, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     'auth', |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     request_token_url=None, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     access_token_method='POST', |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     access_token_url=ACCESS_TOKEN_URL, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     authorize_url=AUTHORIZE_URL, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | ) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | oauth.remote_apps['auth'] = auth |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def verify_token(token): |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     if not token: |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         return False |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     r = requests.get(TOKENINFO_URL, headers={'Authorization': token}) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     return r.status_code == 200 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def authorize(f): |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     @wraps(f) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     def wrapper(*args, **kwargs): |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         if AUTHORIZE_URL and 'auth_token' not in session: |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             return redirect(urljoin(APP_URL, '/login')) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         return f(*args, **kwargs) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     return wrapper |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def ok(body={}, status=200): |  |  |  | def ok(body={}, status=200): | 
			
		
	
		
		
			
				
					
					|  |  |  |     return ( |  |  |  |     return ( | 
			
		
	
		
		
			
				
					
					|  |  |  |         Response( |  |  |  |         Response( | 
			
		
	
	
		
		
			
				
					|  |  | @ -297,19 +256,16 @@ STATIC_HEADERS = { | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/css/<path:path>') |  |  |  | @app.route('/css/<path:path>') | 
			
		
	
		
		
			
				
					
					|  |  |  | @authorize |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def send_css(path): |  |  |  | def send_css(path): | 
			
		
	
		
		
			
				
					
					|  |  |  |     return send_from_directory('static/', path), 200, STATIC_HEADERS |  |  |  |     return send_from_directory('static/', path), 200, STATIC_HEADERS | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/js/<path:path>') |  |  |  | @app.route('/js/<path:path>') | 
			
		
	
		
		
			
				
					
					|  |  |  | @authorize |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def send_js(path): |  |  |  | def send_js(path): | 
			
		
	
		
		
			
				
					
					|  |  |  |     return send_from_directory('static/', path), 200, STATIC_HEADERS |  |  |  |     return send_from_directory('static/', path), 200, STATIC_HEADERS | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/') |  |  |  | @app.route('/') | 
			
		
	
		
		
			
				
					
					|  |  |  | @authorize |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def index(): |  |  |  | def index(): | 
			
		
	
		
		
			
				
					
					|  |  |  |     return render_template('index.html', google_analytics=GOOGLE_ANALYTICS) |  |  |  |     return render_template('index.html', google_analytics=GOOGLE_ANALYTICS) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  |  | @ -345,7 +301,6 @@ DEFAULT_UI_CONFIG = { | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/config') |  |  |  | @app.route('/config') | 
			
		
	
		
		
			
				
					
					|  |  |  | @authorize |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def get_config(): |  |  |  | def get_config(): | 
			
		
	
		
		
			
				
					
					|  |  |  |     config = DEFAULT_UI_CONFIG.copy()  |  |  |  |     config = DEFAULT_UI_CONFIG.copy()  | 
			
		
	
		
		
			
				
					
					|  |  |  |     config.update(OPERATOR_UI_CONFIG) |  |  |  |     config.update(OPERATOR_UI_CONFIG) | 
			
		
	
	
		
		
			
				
					|  |  | @ -407,17 +362,15 @@ def get_teams_for_user(user_name): | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/teams') |  |  |  | @app.route('/teams') | 
			
		
	
		
		
			
				
					
					|  |  |  | @authorize |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def get_teams(): |  |  |  | def get_teams(): | 
			
		
	
		
		
			
				
					
					|  |  |  |     return ok( |  |  |  |     return ok( | 
			
		
	
		
		
			
				
					
					|  |  |  |         get_teams_for_user( |  |  |  |         get_teams_for_user( | 
			
		
	
		
		
			
				
					
					|  |  |  |             session.get('user_name', ''), |  |  |  |             request.headers.get('X-Uid', ''), | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         ) |  |  |  |         ) | 
			
		
	
		
		
			
				
					
					|  |  |  |     ) |  |  |  |     ) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/services/<namespace>/<cluster>') |  |  |  | @app.route('/services/<namespace>/<cluster>') | 
			
		
	
		
		
			
				
					
					|  |  |  | @authorize |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def get_service(namespace: str, cluster: str): |  |  |  | def get_service(namespace: str, cluster: str): | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     if TARGET_NAMESPACE not in ['', '*', namespace]: |  |  |  |     if TARGET_NAMESPACE not in ['', '*', namespace]: | 
			
		
	
	
		
		
			
				
					|  |  | @ -433,7 +386,6 @@ def get_service(namespace: str, cluster: str): | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/pooler/<namespace>/<cluster>') |  |  |  | @app.route('/pooler/<namespace>/<cluster>') | 
			
		
	
		
		
			
				
					
					|  |  |  | @authorize |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def get_list_poolers(namespace: str, cluster: str): |  |  |  | def get_list_poolers(namespace: str, cluster: str): | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     if TARGET_NAMESPACE not in ['', '*', namespace]: |  |  |  |     if TARGET_NAMESPACE not in ['', '*', namespace]: | 
			
		
	
	
		
		
			
				
					|  |  | @ -449,7 +401,6 @@ def get_list_poolers(namespace: str, cluster: str): | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/statefulsets/<namespace>/<cluster>') |  |  |  | @app.route('/statefulsets/<namespace>/<cluster>') | 
			
		
	
		
		
			
				
					
					|  |  |  | @authorize |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def get_list_clusters(namespace: str, cluster: str): |  |  |  | def get_list_clusters(namespace: str, cluster: str): | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     if TARGET_NAMESPACE not in ['', '*', namespace]: |  |  |  |     if TARGET_NAMESPACE not in ['', '*', namespace]: | 
			
		
	
	
		
		
			
				
					|  |  | @ -465,7 +416,6 @@ def get_list_clusters(namespace: str, cluster: str): | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/statefulsets/<namespace>/<cluster>/pods') |  |  |  | @app.route('/statefulsets/<namespace>/<cluster>/pods') | 
			
		
	
		
		
			
				
					
					|  |  |  | @authorize |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def get_list_members(namespace: str, cluster: str): |  |  |  | def get_list_members(namespace: str, cluster: str): | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     if TARGET_NAMESPACE not in ['', '*', namespace]: |  |  |  |     if TARGET_NAMESPACE not in ['', '*', namespace]: | 
			
		
	
	
		
		
			
				
					|  |  | @ -485,7 +435,6 @@ def get_list_members(namespace: str, cluster: str): | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/namespaces') |  |  |  | @app.route('/namespaces') | 
			
		
	
		
		
			
				
					
					|  |  |  | @authorize |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def get_namespaces(): |  |  |  | def get_namespaces(): | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     if TARGET_NAMESPACE not in ['', '*']: |  |  |  |     if TARGET_NAMESPACE not in ['', '*']: | 
			
		
	
	
		
		
			
				
					|  |  | @ -503,7 +452,6 @@ def get_namespaces(): | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/postgresqls') |  |  |  | @app.route('/postgresqls') | 
			
		
	
		
		
			
				
					
					|  |  |  | @authorize |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def get_postgresqls(): |  |  |  | def get_postgresqls(): | 
			
		
	
		
		
			
				
					
					|  |  |  |     postgresqls = [ |  |  |  |     postgresqls = [ | 
			
		
	
		
		
			
				
					
					|  |  |  |         { |  |  |  |         { | 
			
		
	
	
		
		
			
				
					|  |  | @ -602,7 +550,6 @@ def read_only(handler): | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/postgresqls/<namespace>/<cluster>', methods=['POST']) |  |  |  | @app.route('/postgresqls/<namespace>/<cluster>', methods=['POST']) | 
			
		
	
		
		
			
				
					
					|  |  |  | @authorize |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | @namespaced |  |  |  | @namespaced | 
			
		
	
		
		
			
				
					
					|  |  |  | def update_postgresql(namespace: str, cluster: str): |  |  |  | def update_postgresql(namespace: str, cluster: str): | 
			
		
	
		
		
			
				
					
					|  |  |  |     if READ_ONLY_MODE: |  |  |  |     if READ_ONLY_MODE: | 
			
		
	
	
		
		
			
				
					|  |  | @ -614,8 +561,8 @@ def update_postgresql(namespace: str, cluster: str): | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     postgresql = request.get_json(force=True) |  |  |  |     postgresql = request.get_json(force=True) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     teams = get_teams_for_user(session.get('user_name', '')) |  |  |  |     teams = get_teams_for_user(request.headers.get('X-Uid', '')) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     logger.info(f'Changes to: {cluster} by {session.get("user_name", "local-user")}/{teams} {postgresql}')  # noqa |  |  |  |     logger.info(f'Changes to: {cluster} by {request.headers.get("X-Uid", "local-user")}/{teams} {postgresql}')  # noqa | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     if SUPERUSER_TEAM and SUPERUSER_TEAM in teams: |  |  |  |     if SUPERUSER_TEAM and SUPERUSER_TEAM in teams: | 
			
		
	
		
		
			
				
					
					|  |  |  |         logger.info(f'Allowing edit due to membership in superuser team {SUPERUSER_TEAM}')  # noqa |  |  |  |         logger.info(f'Allowing edit due to membership in superuser team {SUPERUSER_TEAM}')  # noqa | 
			
		
	
	
		
		
			
				
					|  |  | @ -810,7 +757,6 @@ def update_postgresql(namespace: str, cluster: str): | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/postgresqls/<namespace>/<cluster>', methods=['GET']) |  |  |  | @app.route('/postgresqls/<namespace>/<cluster>', methods=['GET']) | 
			
		
	
		
		
			
				
					
					|  |  |  | @authorize |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def get_postgresql(namespace: str, cluster: str): |  |  |  | def get_postgresql(namespace: str, cluster: str): | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     if TARGET_NAMESPACE not in ['', '*', namespace]: |  |  |  |     if TARGET_NAMESPACE not in ['', '*', namespace]: | 
			
		
	
	
		
		
			
				
					|  |  | @ -826,7 +772,6 @@ def get_postgresql(namespace: str, cluster: str): | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/stored_clusters') |  |  |  | @app.route('/stored_clusters') | 
			
		
	
		
		
			
				
					
					|  |  |  | @authorize |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def get_stored_clusters(): |  |  |  | def get_stored_clusters(): | 
			
		
	
		
		
			
				
					
					|  |  |  |     return respond( |  |  |  |     return respond( | 
			
		
	
		
		
			
				
					
					|  |  |  |         read_stored_clusters( |  |  |  |         read_stored_clusters( | 
			
		
	
	
		
		
			
				
					|  |  | @ -837,7 +782,6 @@ def get_stored_clusters(): | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/stored_clusters/<pg_cluster>', methods=['GET']) |  |  |  | @app.route('/stored_clusters/<pg_cluster>', methods=['GET']) | 
			
		
	
		
		
			
				
					
					|  |  |  | @authorize |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def get_versions(pg_cluster: str): |  |  |  | def get_versions(pg_cluster: str): | 
			
		
	
		
		
			
				
					
					|  |  |  |     return respond( |  |  |  |     return respond( | 
			
		
	
		
		
			
				
					
					|  |  |  |         read_versions( |  |  |  |         read_versions( | 
			
		
	
	
		
		
			
				
					|  |  | @ -850,9 +794,7 @@ def get_versions(pg_cluster: str): | 
			
		
	
		
		
			
				
					
					|  |  |  |     ) |  |  |  |     ) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/stored_clusters/<pg_cluster>/<uid>', methods=['GET']) |  |  |  | @app.route('/stored_clusters/<pg_cluster>/<uid>', methods=['GET']) | 
			
		
	
		
		
			
				
					
					|  |  |  | @authorize |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def get_basebackups(pg_cluster: str, uid: str): |  |  |  | def get_basebackups(pg_cluster: str, uid: str): | 
			
		
	
		
		
			
				
					
					|  |  |  |     return respond( |  |  |  |     return respond( | 
			
		
	
		
		
			
				
					
					|  |  |  |         read_basebackups( |  |  |  |         read_basebackups( | 
			
		
	
	
		
		
			
				
					|  |  | @ -867,7 +809,6 @@ def get_basebackups(pg_cluster: str, uid: str): | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/create-cluster', methods=['POST']) |  |  |  | @app.route('/create-cluster', methods=['POST']) | 
			
		
	
		
		
			
				
					
					|  |  |  | @authorize |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def create_new_cluster(): |  |  |  | def create_new_cluster(): | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     if READ_ONLY_MODE: |  |  |  |     if READ_ONLY_MODE: | 
			
		
	
	
		
		
			
				
					|  |  | @ -885,8 +826,8 @@ def create_new_cluster(): | 
			
		
	
		
		
			
				
					
					|  |  |  |     if TARGET_NAMESPACE not in ['', '*', namespace]: |  |  |  |     if TARGET_NAMESPACE not in ['', '*', namespace]: | 
			
		
	
		
		
			
				
					
					|  |  |  |         return wrong_namespace() |  |  |  |         return wrong_namespace() | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     teams = get_teams_for_user(session.get('user_name', '')) |  |  |  |     teams = get_teams_for_user(request.headers.get('X-Uid', '')) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     logger.info(f'Create cluster by {session.get("user_name", "local-user")}/{teams} {postgresql}')  # noqa |  |  |  |     logger.info(f'Create cluster by {request.headers.get("X-Uid", "local-user")}/{teams} {postgresql}')  # noqa | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     if SUPERUSER_TEAM and SUPERUSER_TEAM in teams: |  |  |  |     if SUPERUSER_TEAM and SUPERUSER_TEAM in teams: | 
			
		
	
		
		
			
				
					
					|  |  |  |         logger.info(f'Allowing create due to membership in superuser team {SUPERUSER_TEAM}')  # noqa |  |  |  |         logger.info(f'Allowing create due to membership in superuser team {SUPERUSER_TEAM}')  # noqa | 
			
		
	
	
		
		
			
				
					|  |  | @ -898,7 +839,6 @@ def create_new_cluster(): | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/postgresqls/<namespace>/<cluster>', methods=['DELETE']) |  |  |  | @app.route('/postgresqls/<namespace>/<cluster>', methods=['DELETE']) | 
			
		
	
		
		
			
				
					
					|  |  |  | @authorize |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def delete_postgresql(namespace: str, cluster: str): |  |  |  | def delete_postgresql(namespace: str, cluster: str): | 
			
		
	
		
		
			
				
					
					|  |  |  |     if TARGET_NAMESPACE not in ['', '*', namespace]: |  |  |  |     if TARGET_NAMESPACE not in ['', '*', namespace]: | 
			
		
	
		
		
			
				
					
					|  |  |  |         return wrong_namespace() |  |  |  |         return wrong_namespace() | 
			
		
	
	
		
		
			
				
					|  |  | @ -910,9 +850,9 @@ def delete_postgresql(namespace: str, cluster: str): | 
			
		
	
		
		
			
				
					
					|  |  |  |     if postgresql is None: |  |  |  |     if postgresql is None: | 
			
		
	
		
		
			
				
					
					|  |  |  |         return not_found() |  |  |  |         return not_found() | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     teams = get_teams_for_user(session.get('user_name', '')) |  |  |  |     teams = get_teams_for_user(request.headers.get('X-Uid', '')) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     logger.info(f'Delete cluster: {cluster} by {session.get("user_name", "local-user")}/{teams}')  # noqa |  |  |  |     logger.info(f'Delete cluster: {cluster} by {request.headers.get("X-Uid", "local-user")}/{teams}')  # noqa | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     if SUPERUSER_TEAM and SUPERUSER_TEAM in teams: |  |  |  |     if SUPERUSER_TEAM and SUPERUSER_TEAM in teams: | 
			
		
	
		
		
			
				
					
					|  |  |  |         logger.info(f'Allowing delete due to membership in superuser team {SUPERUSER_TEAM}')  # noqa |  |  |  |         logger.info(f'Allowing delete due to membership in superuser team {SUPERUSER_TEAM}')  # noqa | 
			
		
	
	
		
		
			
				
					|  |  | @ -936,78 +876,30 @@ def proxy_operator(url: str): | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/operator/status') |  |  |  | @app.route('/operator/status') | 
			
		
	
		
		
			
				
					
					|  |  |  | @authorize |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def get_operator_status(): |  |  |  | def get_operator_status(): | 
			
		
	
		
		
			
				
					
					|  |  |  |     return proxy_operator('/status/') |  |  |  |     return proxy_operator('/status/') | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/operator/workers/<worker>/queue') |  |  |  | @app.route('/operator/workers/<worker>/queue') | 
			
		
	
		
		
			
				
					
					|  |  |  | @authorize |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def get_operator_get_queue(worker: int): |  |  |  | def get_operator_get_queue(worker: int): | 
			
		
	
		
		
			
				
					
					|  |  |  |     return proxy_operator(f'/workers/{worker}/queue') |  |  |  |     return proxy_operator(f'/workers/{worker}/queue') | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/operator/workers/<worker>/logs') |  |  |  | @app.route('/operator/workers/<worker>/logs') | 
			
		
	
		
		
			
				
					
					|  |  |  | @authorize |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def get_operator_get_logs(worker: int): |  |  |  | def get_operator_get_logs(worker: int): | 
			
		
	
		
		
			
				
					
					|  |  |  |     return proxy_operator(f'/workers/{worker}/logs') |  |  |  |     return proxy_operator(f'/workers/{worker}/logs') | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/operator/clusters/<namespace>/<cluster>/logs') |  |  |  | @app.route('/operator/clusters/<namespace>/<cluster>/logs') | 
			
		
	
		
		
			
				
					
					|  |  |  | @authorize |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def get_operator_get_logs_per_cluster(namespace: str, cluster: str): |  |  |  | def get_operator_get_logs_per_cluster(namespace: str, cluster: str): | 
			
		
	
		
		
			
				
					
					|  |  |  |     return proxy_operator(f'/clusters/{namespace}/{cluster}/logs/') |  |  |  |     return proxy_operator(f'/clusters/{namespace}/{cluster}/logs/') | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/login') |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def login(): |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     redirect = request.args.get('redirect', False) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     if not redirect: |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         return render_template('login-deeplink.html') |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     redirect_uri = urljoin(APP_URL, '/login/authorized') |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     return auth.authorize(callback=redirect_uri) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/logout') |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def logout(): |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     session.pop('auth_token', None) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     return redirect(urljoin(APP_URL, '/')) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/favicon.png') |  |  |  | @app.route('/favicon.png') | 
			
		
	
		
		
			
				
					
					|  |  |  | def favicon(): |  |  |  | def favicon(): | 
			
		
	
		
		
			
				
					
					|  |  |  |     return send_from_directory('static/', 'favicon-96x96.png'), 200 |  |  |  |     return send_from_directory('static/', 'favicon-96x96.png'), 200 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | @app.route('/login/authorized') |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def authorized(): |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     resp = auth.authorized_response() |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     if resp is None: |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         return 'Access denied: reason=%s error=%s' % ( |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             request.args['error'], |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             request.args['error_description'] |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         ) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     if not isinstance(resp, dict): |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         return 'Invalid auth response' |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     session['auth_token'] = (resp['access_token'], '') |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     r = requests.get( |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         TOKENINFO_URL, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         headers={ |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             'Authorization': f'Bearer {session["auth_token"][0]}', |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         }, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     ) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     session['user_name'] = r.json().get('uid') |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     logger.info(f'Login from: {session["user_name"]}') |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     # return redirect(urljoin(APP_URL, '/')) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     return render_template('login-resolve-deeplink.html') |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | def shutdown(): |  |  |  | def shutdown(): | 
			
		
	
		
		
			
				
					
					|  |  |  |     # just wait some time to give Kubernetes time to update endpoints |  |  |  |     # just wait some time to give Kubernetes time to update endpoints | 
			
		
	
		
		
			
				
					
					|  |  |  |     # this requires changing the readinessProbe's |  |  |  |     # this requires changing the readinessProbe's | 
			
		
	
	
		
		
			
				
					|  |  | @ -1083,28 +975,20 @@ def init_cluster(): | 
			
		
	
		
		
			
				
					
					|  |  |  |     help='Verbose logging', |  |  |  |     help='Verbose logging', | 
			
		
	
		
		
			
				
					
					|  |  |  |     is_flag=True, |  |  |  |     is_flag=True, | 
			
		
	
		
		
			
				
					
					|  |  |  | ) |  |  |  | ) | 
			
		
	
		
		
			
				
					
					|  |  |  | @option( |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     '--secret-key', |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     default='development', |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     envvar='SECRET_KEY', |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     help='Secret key for session cookies', |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | ) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | @option( |  |  |  | @option( | 
			
		
	
		
		
			
				
					
					|  |  |  |     '--clusters', |  |  |  |     '--clusters', | 
			
		
	
		
		
			
				
					
					|  |  |  |     envvar='CLUSTERS', |  |  |  |     envvar='CLUSTERS', | 
			
		
	
		
		
			
				
					
					|  |  |  |     help=f'Comma separated list of Kubernetes API server URLs (default: {DEFAULT_CLUSTERS})',  # noqa |  |  |  |     help=f'Comma separated list of Kubernetes API server URLs (default: {DEFAULT_CLUSTERS})',  # noqa | 
			
		
	
		
		
			
				
					
					|  |  |  |     type=CommaSeparatedValues(), |  |  |  |     type=CommaSeparatedValues(), | 
			
		
	
		
		
			
				
					
					|  |  |  | ) |  |  |  | ) | 
			
		
	
		
		
			
				
					
					|  |  |  | def main(port, secret_key, debug, clusters: list): |  |  |  | def main(port, debug, clusters: list): | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     global TARGET_NAMESPACE |  |  |  |     global TARGET_NAMESPACE | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     basicConfig(stream=sys.stdout, level=(DEBUG if debug else INFO), format='%(asctime)s %(levelname)s: %(message)s',) |  |  |  |     basicConfig(stream=sys.stdout, level=(DEBUG if debug else INFO), format='%(asctime)s %(levelname)s: %(message)s',) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     init_cluster() |  |  |  |     init_cluster() | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     logger.info(f'Access token URL: {ACCESS_TOKEN_URL}') |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     logger.info(f'App URL: {APP_URL}') |  |  |  |     logger.info(f'App URL: {APP_URL}') | 
			
		
	
		
		
			
				
					
					|  |  |  |     logger.info(f'Authorize URL: {AUTHORIZE_URL}') |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     logger.info(f'Operator API URL: {OPERATOR_API_URL}') |  |  |  |     logger.info(f'Operator API URL: {OPERATOR_API_URL}') | 
			
		
	
		
		
			
				
					
					|  |  |  |     logger.info(f'Operator cluster name label: {OPERATOR_CLUSTER_NAME_LABEL}') |  |  |  |     logger.info(f'Operator cluster name label: {OPERATOR_CLUSTER_NAME_LABEL}') | 
			
		
	
		
		
			
				
					
					|  |  |  |     logger.info(f'Readonly mode: {"enabled" if READ_ONLY_MODE else "disabled"}')  # noqa |  |  |  |     logger.info(f'Readonly mode: {"enabled" if READ_ONLY_MODE else "disabled"}')  # noqa | 
			
		
	
	
		
		
			
				
					|  |  | @ -1113,7 +997,6 @@ def main(port, secret_key, debug, clusters: list): | 
			
		
	
		
		
			
				
					
					|  |  |  |     logger.info(f'Superuser team: {SUPERUSER_TEAM}') |  |  |  |     logger.info(f'Superuser team: {SUPERUSER_TEAM}') | 
			
		
	
		
		
			
				
					
					|  |  |  |     logger.info(f'Target namespace: {TARGET_NAMESPACE}') |  |  |  |     logger.info(f'Target namespace: {TARGET_NAMESPACE}') | 
			
		
	
		
		
			
				
					
					|  |  |  |     logger.info(f'Teamservice URL: {TEAM_SERVICE_URL}') |  |  |  |     logger.info(f'Teamservice URL: {TEAM_SERVICE_URL}') | 
			
		
	
		
		
			
				
					
					|  |  |  |     logger.info(f'Tokeninfo URL: {TOKENINFO_URL}') |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     logger.info(f'Use AWS instance_profile: {USE_AWS_INSTANCE_PROFILE}') |  |  |  |     logger.info(f'Use AWS instance_profile: {USE_AWS_INSTANCE_PROFILE}') | 
			
		
	
		
		
			
				
					
					|  |  |  |     logger.info(f'WAL-E S3 endpoint: {WALE_S3_ENDPOINT}') |  |  |  |     logger.info(f'WAL-E S3 endpoint: {WALE_S3_ENDPOINT}') | 
			
		
	
		
		
			
				
					
					|  |  |  |     logger.info(f'AWS S3 endpoint: {AWS_ENDPOINT}') |  |  |  |     logger.info(f'AWS S3 endpoint: {AWS_ENDPOINT}') | 
			
		
	
	
		
		
			
				
					|  |  | @ -1136,7 +1019,6 @@ def main(port, secret_key, debug, clusters: list): | 
			
		
	
		
		
			
				
					
					|  |  |  |         logger.info(f'Target namespace set to: {TARGET_NAMESPACE or "*"}') |  |  |  |         logger.info(f'Target namespace set to: {TARGET_NAMESPACE or "*"}') | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     app.debug = debug |  |  |  |     app.debug = debug | 
			
		
	
		
		
			
				
					
					|  |  |  |     app.secret_key = secret_key |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     signal(SIGTERM, exit_gracefully) |  |  |  |     signal(SIGTERM, exit_gracefully) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  |  | 
 |