mirror of
https://github.com/zalando/postgres-operator.git
synced 2026-10-06 08:22:40 +02:00
Initial commit for Postgres Operator UI (#613)
* Initial commit for our basic Postgres Operator UI: * Create and modify Postgres manifests * Watch Operator Logs in the UI * Observe cluster creation progress * S3 Backup browser for clone and restore Many thanks to Manuel Gomez and Jan Mussler for the initial UI work a long time ago!
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
import logging
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import kubernetes.client
|
||||
import kubernetes.config
|
||||
import tokens
|
||||
from requests.auth import AuthBase
|
||||
|
||||
# default URL points to kubectl proxy
|
||||
DEFAULT_CLUSTERS = 'http://localhost:8001/'
|
||||
CLUSTER_ID_INVALID_CHARS = re.compile('[^a-z0-9:-]')
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
tokens.configure(from_file_only=True)
|
||||
|
||||
|
||||
def generate_cluster_id(url: str):
|
||||
'''Generate some "cluster ID" from given API server URL'''
|
||||
for prefix in ('https://', 'http://'):
|
||||
if url.startswith(prefix):
|
||||
url = url[len(prefix):]
|
||||
return CLUSTER_ID_INVALID_CHARS.sub('-', url.lower()).strip('-')
|
||||
|
||||
|
||||
class StaticAuthorizationHeaderAuth(AuthBase):
|
||||
'''Static authentication with given "Authorization" header'''
|
||||
|
||||
def __init__(self, authorization):
|
||||
self.authorization = authorization
|
||||
|
||||
def __call__(self, request):
|
||||
request.headers['Authorization'] = self.authorization
|
||||
return request
|
||||
|
||||
|
||||
class OAuthTokenAuth(AuthBase):
|
||||
'''Dynamic authentication using the "tokens" library to load OAuth tokens from file
|
||||
(potentially mounted from a Kubernetes secret)'''
|
||||
|
||||
def __init__(self, token_name):
|
||||
self.token_name = token_name
|
||||
tokens.manage(token_name)
|
||||
|
||||
def __call__(self, request):
|
||||
token = tokens.get(self.token_name)
|
||||
request.headers['Authorization'] = 'Bearer {}'.format(token)
|
||||
return request
|
||||
|
||||
|
||||
class Cluster:
|
||||
def __init__(self, id, api_server_url, ssl_ca_cert=None, auth=None, cert_file=None, key_file=None):
|
||||
self.id = id
|
||||
self.api_server_url = api_server_url
|
||||
self.ssl_ca_cert = ssl_ca_cert
|
||||
self.auth = auth
|
||||
self.cert_file = cert_file
|
||||
self.key_file = key_file
|
||||
|
||||
|
||||
class StaticClusterDiscoverer:
|
||||
|
||||
def __init__(self, api_server_urls: list):
|
||||
self._clusters = []
|
||||
|
||||
if not api_server_urls:
|
||||
try:
|
||||
kubernetes.config.load_incluster_config()
|
||||
except kubernetes.config.ConfigException:
|
||||
# we are not running inside a cluster
|
||||
# => assume default kubectl proxy URL
|
||||
cluster = Cluster(generate_cluster_id(DEFAULT_CLUSTERS), DEFAULT_CLUSTERS)
|
||||
else:
|
||||
logger.info("in cluster configuration failed")
|
||||
config = kubernetes.client.configuration
|
||||
cluster = Cluster(
|
||||
generate_cluster_id(config.host),
|
||||
config.host,
|
||||
ssl_ca_cert=config.ssl_ca_cert,
|
||||
auth=StaticAuthorizationHeaderAuth(config.api_key['authorization']))
|
||||
self._clusters.append(cluster)
|
||||
else:
|
||||
for api_server_url in api_server_urls:
|
||||
logger.info("api server url: {}".format(api_server_url))
|
||||
if 'localhost' not in api_server_url:
|
||||
# TODO: hacky way of detecting whether we need a token or not
|
||||
auth = OAuthTokenAuth('read-only')
|
||||
else:
|
||||
auth = None
|
||||
self._clusters.append(Cluster(generate_cluster_id(api_server_url), api_server_url, auth=auth))
|
||||
|
||||
def get_clusters(self):
|
||||
return self._clusters
|
||||
|
||||
|
||||
class KubeconfigDiscoverer:
|
||||
|
||||
def __init__(self, kubeconfig_path: Path, contexts: set):
|
||||
self._path = kubeconfig_path
|
||||
self._contexts = contexts
|
||||
|
||||
def get_clusters(self):
|
||||
# Kubernetes Python client expects "vintage" string path
|
||||
config_file = str(self._path)
|
||||
contexts, current_context = kubernetes.config.list_kube_config_contexts(config_file)
|
||||
for context in contexts:
|
||||
if self._contexts and context['name'] not in self._contexts:
|
||||
# filter out
|
||||
continue
|
||||
config = kubernetes.client.ConfigurationObject()
|
||||
kubernetes.config.load_kube_config(config_file, context=context['name'], client_configuration=config)
|
||||
authorization = config.api_key.get('authorization')
|
||||
if authorization:
|
||||
auth = StaticAuthorizationHeaderAuth(authorization)
|
||||
else:
|
||||
auth = None
|
||||
cluster = Cluster(
|
||||
context['name'],
|
||||
config.host,
|
||||
ssl_ca_cert=config.ssl_ca_cert,
|
||||
cert_file=config.cert_file,
|
||||
key_file=config.key_file,
|
||||
auth=auth)
|
||||
yield cluster
|
||||
Reference in New Issue
Block a user