[UI] add toggle for connection pooler (#953)
* [UI] add toggle for connection pooler * remove team service logger * fix new.tag.pug and change port in Makefile
This commit is contained in:
parent
865d5b41a7
commit
5af4379118
|
|
@ -1,7 +1,7 @@
|
||||||
FROM alpine:3.6
|
FROM alpine:3.6
|
||||||
MAINTAINER team-acid@zalando.de
|
MAINTAINER team-acid@zalando.de
|
||||||
|
|
||||||
EXPOSE 8080
|
EXPOSE 8081
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
apk add --no-cache \
|
apk add --no-cache \
|
||||||
|
|
@ -29,6 +29,7 @@ RUN \
|
||||||
/var/cache/apk/*
|
/var/cache/apk/*
|
||||||
|
|
||||||
COPY requirements.txt /
|
COPY requirements.txt /
|
||||||
|
COPY start_server.sh /
|
||||||
RUN pip3 install -r /requirements.txt
|
RUN pip3 install -r /requirements.txt
|
||||||
|
|
||||||
COPY operator_ui /operator_ui
|
COPY operator_ui /operator_ui
|
||||||
|
|
@ -37,4 +38,4 @@ ARG VERSION=dev
|
||||||
RUN sed -i "s/__version__ = .*/__version__ = '${VERSION}'/" /operator_ui/__init__.py
|
RUN sed -i "s/__version__ = .*/__version__ = '${VERSION}'/" /operator_ui/__init__.py
|
||||||
|
|
||||||
WORKDIR /
|
WORKDIR /
|
||||||
ENTRYPOINT ["/usr/bin/python3", "-m", "operator_ui"]
|
CMD ["/usr/bin/python3", "-m", "operator_ui"]
|
||||||
|
|
|
||||||
|
|
@ -36,4 +36,4 @@ push:
|
||||||
docker push "$(IMAGE):$(TAG)$(CDP_TAG)"
|
docker push "$(IMAGE):$(TAG)$(CDP_TAG)"
|
||||||
|
|
||||||
mock:
|
mock:
|
||||||
docker run -it -p 8080:8080 "$(IMAGE):$(TAG)" --mock
|
docker run -it -p 8081:8081 "$(IMAGE):$(TAG)" --mock
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,7 @@ edit
|
||||||
o.spec.numberOfInstances = i.spec.numberOfInstances
|
o.spec.numberOfInstances = i.spec.numberOfInstances
|
||||||
o.spec.enableMasterLoadBalancer = i.spec.enableMasterLoadBalancer || false
|
o.spec.enableMasterLoadBalancer = i.spec.enableMasterLoadBalancer || false
|
||||||
o.spec.enableReplicaLoadBalancer = i.spec.enableReplicaLoadBalancer || false
|
o.spec.enableReplicaLoadBalancer = i.spec.enableReplicaLoadBalancer || false
|
||||||
|
o.spec.enableConnectionPooler = i.spec.enableConnectionPooler || false
|
||||||
o.spec.volume = { size: i.spec.volume.size }
|
o.spec.volume = { size: i.spec.volume.size }
|
||||||
|
|
||||||
if ('users' in i.spec && typeof i.spec.users === 'object') {
|
if ('users' in i.spec && typeof i.spec.users === 'object') {
|
||||||
|
|
|
||||||
|
|
@ -239,6 +239,18 @@ new
|
||||||
|
|
|
|
||||||
| Enable replica ELB
|
| Enable replica ELB
|
||||||
|
|
||||||
|
tr
|
||||||
|
td Enable Connection Pool
|
||||||
|
td
|
||||||
|
label
|
||||||
|
input(
|
||||||
|
type='checkbox'
|
||||||
|
value='{ enableConnectionPooler }'
|
||||||
|
onchange='{ toggleEnableConnectionPooler }'
|
||||||
|
)
|
||||||
|
|
|
||||||
|
| Enable Connection Pool (using PGBouncer)
|
||||||
|
|
||||||
tr
|
tr
|
||||||
td Volume size
|
td Volume size
|
||||||
td
|
td
|
||||||
|
|
@ -493,6 +505,9 @@ new
|
||||||
{{#if enableReplicaLoadBalancer}}
|
{{#if enableReplicaLoadBalancer}}
|
||||||
enableReplicaLoadBalancer: true
|
enableReplicaLoadBalancer: true
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{#if enableConnectionPooler}}
|
||||||
|
enableConnectionPooler: true
|
||||||
|
{{/if}}
|
||||||
volume:
|
volume:
|
||||||
size: "{{ volumeSize }}Gi"
|
size: "{{ volumeSize }}Gi"
|
||||||
{{#if users}}
|
{{#if users}}
|
||||||
|
|
@ -516,13 +531,14 @@ new
|
||||||
- {{ odd }}/32
|
- {{ odd }}/32
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if resourcesVisible}}
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
cpu: {{ cpu.state.request.state }}m
|
cpu: {{ cpu.state.request.state }}m
|
||||||
memory: {{ memory.state.request.state }}Mi
|
memory: {{ memory.state.request.state }}Mi
|
||||||
limits:
|
limits:
|
||||||
cpu: {{ cpu.state.limit.state }}m
|
cpu: {{ cpu.state.limit.state }}m
|
||||||
memory: {{ memory.state.limit.state }}Mi{{#if restoring}}
|
memory: {{ memory.state.limit.state }}Mi{{/if}}{{#if restoring}}
|
||||||
|
|
||||||
clone:
|
clone:
|
||||||
cluster: "{{ backup.state.name.state }}"
|
cluster: "{{ backup.state.name.state }}"
|
||||||
|
|
@ -542,6 +558,7 @@ new
|
||||||
instanceCount: this.instanceCount,
|
instanceCount: this.instanceCount,
|
||||||
enableMasterLoadBalancer: this.enableMasterLoadBalancer,
|
enableMasterLoadBalancer: this.enableMasterLoadBalancer,
|
||||||
enableReplicaLoadBalancer: this.enableReplicaLoadBalancer,
|
enableReplicaLoadBalancer: this.enableReplicaLoadBalancer,
|
||||||
|
enableConnectionPooler: this.enableConnectionPooler,
|
||||||
volumeSize: this.volumeSize,
|
volumeSize: this.volumeSize,
|
||||||
users: this.users.valids,
|
users: this.users.valids,
|
||||||
databases: this.databases.valids,
|
databases: this.databases.valids,
|
||||||
|
|
@ -552,6 +569,7 @@ new
|
||||||
memory: this.memory,
|
memory: this.memory,
|
||||||
backup: this.backup,
|
backup: this.backup,
|
||||||
namespace: this.namespace,
|
namespace: this.namespace,
|
||||||
|
resourcesVisible: this.config.resources_visible,
|
||||||
restoring: this.backup.state.type.state !== 'empty',
|
restoring: this.backup.state.type.state !== 'empty',
|
||||||
pitr: this.backup.state.type.state === 'pitr',
|
pitr: this.backup.state.type.state === 'pitr',
|
||||||
}
|
}
|
||||||
|
|
@ -598,6 +616,10 @@ new
|
||||||
this.enableReplicaLoadBalancer = !this.enableReplicaLoadBalancer
|
this.enableReplicaLoadBalancer = !this.enableReplicaLoadBalancer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.toggleEnableConnectionPooler = e => {
|
||||||
|
this.enableConnectionPooler = !this.enableConnectionPooler
|
||||||
|
}
|
||||||
|
|
||||||
this.volumeChange = e => {
|
this.volumeChange = e => {
|
||||||
this.volumeSize = +e.target.value
|
this.volumeSize = +e.target.value
|
||||||
}
|
}
|
||||||
|
|
@ -892,6 +914,7 @@ new
|
||||||
this.odd = ''
|
this.odd = ''
|
||||||
this.enableMasterLoadBalancer = false
|
this.enableMasterLoadBalancer = false
|
||||||
this.enableReplicaLoadBalancer = false
|
this.enableReplicaLoadBalancer = false
|
||||||
|
this.enableConnectionPooler = false
|
||||||
|
|
||||||
this.postgresqlVersion = this.postgresqlVersion = (
|
this.postgresqlVersion = this.postgresqlVersion = (
|
||||||
this.config.postgresql_versions[0]
|
this.config.postgresql_versions[0]
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,8 @@ postgresql
|
||||||
.alert.alert-success(if='{ progress.masterLabel }') PostgreSQL master available, label is attached
|
.alert.alert-success(if='{ progress.masterLabel }') PostgreSQL master available, label is attached
|
||||||
.alert.alert-success(if='{ progress.masterLabel && progress.dnsName }') PostgreSQL ready: <strong>{ progress.dnsName }</strong>
|
.alert.alert-success(if='{ progress.masterLabel && progress.dnsName }') PostgreSQL ready: <strong>{ progress.dnsName }</strong>
|
||||||
|
|
||||||
|
.alert.alert-success(if='{ progress.pooler }') Connection pooler deployment created
|
||||||
|
|
||||||
.col-lg-3
|
.col-lg-3
|
||||||
help-general(config='{ opts.config }')
|
help-general(config='{ opts.config }')
|
||||||
|
|
||||||
|
|
@ -122,9 +124,11 @@ postgresql
|
||||||
jQuery.get(
|
jQuery.get(
|
||||||
'/postgresqls/' + this.cluster_path,
|
'/postgresqls/' + this.cluster_path,
|
||||||
).done(data => {
|
).done(data => {
|
||||||
|
this.progress.pooler = false
|
||||||
this.progress.postgresql = true
|
this.progress.postgresql = true
|
||||||
this.progress.postgresqlManifest = data
|
this.progress.postgresqlManifest = data
|
||||||
this.progress.createdTimestamp = data.metadata.creationTimestamp
|
this.progress.createdTimestamp = data.metadata.creationTimestamp
|
||||||
|
this.progress.poolerEnabled = data.spec.enableConnectionPooler
|
||||||
this.uid = this.progress.postgresqlManifest.metadata.uid
|
this.uid = this.progress.postgresqlManifest.metadata.uid
|
||||||
this.update()
|
this.update()
|
||||||
|
|
||||||
|
|
@ -160,6 +164,11 @@ postgresql
|
||||||
this.progress.dnsName = data.metadata.name + '.' + data.metadata.namespace
|
this.progress.dnsName = data.metadata.name + '.' + data.metadata.namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jQuery.get('/pooler/' + this.cluster_path).done(data => {
|
||||||
|
this.progress.pooler = {"url": ""}
|
||||||
|
this.update()
|
||||||
|
})
|
||||||
|
|
||||||
this.update()
|
this.update()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,8 @@ spec:
|
||||||
value: "http://postgres-operator:8080"
|
value: "http://postgres-operator:8080"
|
||||||
- name: "OPERATOR_CLUSTER_NAME_LABEL"
|
- name: "OPERATOR_CLUSTER_NAME_LABEL"
|
||||||
value: "cluster-name"
|
value: "cluster-name"
|
||||||
|
- name: "RESOURCES_VISIBLE"
|
||||||
|
value: "False"
|
||||||
- name: "TARGET_NAMESPACE"
|
- name: "TARGET_NAMESPACE"
|
||||||
value: "default"
|
value: "default"
|
||||||
- name: "TEAMS"
|
- name: "TEAMS"
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ rules:
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- apps
|
- apps
|
||||||
resources:
|
resources:
|
||||||
|
- deployments
|
||||||
- statefulsets
|
- statefulsets
|
||||||
verbs:
|
verbs:
|
||||||
- get
|
- get
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ from flask import (
|
||||||
from flask_oauthlib.client import OAuth
|
from flask_oauthlib.client import OAuth
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from gevent import sleep, spawn
|
from gevent import sleep, spawn
|
||||||
from gevent.wsgi import WSGIServer
|
from gevent.pywsgi import WSGIServer
|
||||||
from jq import jq
|
from jq import jq
|
||||||
from json import dumps, loads
|
from json import dumps, loads
|
||||||
from logging import DEBUG, ERROR, INFO, basicConfig, exception, getLogger
|
from logging import DEBUG, ERROR, INFO, basicConfig, exception, getLogger
|
||||||
|
|
@ -44,6 +44,7 @@ from .spiloutils import (
|
||||||
create_postgresql,
|
create_postgresql,
|
||||||
read_basebackups,
|
read_basebackups,
|
||||||
read_namespaces,
|
read_namespaces,
|
||||||
|
read_pooler,
|
||||||
read_pods,
|
read_pods,
|
||||||
read_postgresql,
|
read_postgresql,
|
||||||
read_postgresqls,
|
read_postgresqls,
|
||||||
|
|
@ -80,6 +81,7 @@ OPERATOR_CLUSTER_NAME_LABEL = getenv('OPERATOR_CLUSTER_NAME_LABEL', 'cluster-nam
|
||||||
OPERATOR_UI_CONFIG = getenv('OPERATOR_UI_CONFIG', '{}')
|
OPERATOR_UI_CONFIG = getenv('OPERATOR_UI_CONFIG', '{}')
|
||||||
OPERATOR_UI_MAINTENANCE_CHECK = getenv('OPERATOR_UI_MAINTENANCE_CHECK', '{}')
|
OPERATOR_UI_MAINTENANCE_CHECK = getenv('OPERATOR_UI_MAINTENANCE_CHECK', '{}')
|
||||||
READ_ONLY_MODE = getenv('READ_ONLY_MODE', False) in [True, 'true']
|
READ_ONLY_MODE = getenv('READ_ONLY_MODE', False) in [True, 'true']
|
||||||
|
RESOURCES_VISIBLE = getenv('RESOURCES_VISIBLE', True)
|
||||||
SPILO_S3_BACKUP_PREFIX = getenv('SPILO_S3_BACKUP_PREFIX', 'spilo/')
|
SPILO_S3_BACKUP_PREFIX = getenv('SPILO_S3_BACKUP_PREFIX', 'spilo/')
|
||||||
SUPERUSER_TEAM = getenv('SUPERUSER_TEAM', 'acid')
|
SUPERUSER_TEAM = getenv('SUPERUSER_TEAM', 'acid')
|
||||||
TARGET_NAMESPACE = getenv('TARGET_NAMESPACE')
|
TARGET_NAMESPACE = getenv('TARGET_NAMESPACE')
|
||||||
|
|
@ -312,6 +314,7 @@ DEFAULT_UI_CONFIG = {
|
||||||
def get_config():
|
def get_config():
|
||||||
config = loads(OPERATOR_UI_CONFIG) or DEFAULT_UI_CONFIG
|
config = loads(OPERATOR_UI_CONFIG) or DEFAULT_UI_CONFIG
|
||||||
config['read_only_mode'] = READ_ONLY_MODE
|
config['read_only_mode'] = READ_ONLY_MODE
|
||||||
|
config['resources_visible'] = RESOURCES_VISIBLE
|
||||||
config['superuser_team'] = SUPERUSER_TEAM
|
config['superuser_team'] = SUPERUSER_TEAM
|
||||||
config['target_namespace'] = TARGET_NAMESPACE
|
config['target_namespace'] = TARGET_NAMESPACE
|
||||||
|
|
||||||
|
|
@ -397,6 +400,22 @@ def get_service(namespace: str, cluster: str):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/pooler/<namespace>/<cluster>')
|
||||||
|
@authorize
|
||||||
|
def get_list_poolers(namespace: str, cluster: str):
|
||||||
|
|
||||||
|
if TARGET_NAMESPACE not in ['', '*', namespace]:
|
||||||
|
return wrong_namespace()
|
||||||
|
|
||||||
|
return respond(
|
||||||
|
read_pooler(
|
||||||
|
get_cluster(),
|
||||||
|
namespace,
|
||||||
|
"{}-pooler".format(cluster),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/statefulsets/<namespace>/<cluster>')
|
@app.route('/statefulsets/<namespace>/<cluster>')
|
||||||
@authorize
|
@authorize
|
||||||
def get_list_clusters(namespace: str, cluster: str):
|
def get_list_clusters(namespace: str, cluster: str):
|
||||||
|
|
@ -587,6 +606,17 @@ def update_postgresql(namespace: str, cluster: str):
|
||||||
|
|
||||||
spec['volume'] = {'size': size}
|
spec['volume'] = {'size': size}
|
||||||
|
|
||||||
|
if 'enableConnectionPooler' in postgresql['spec']:
|
||||||
|
cp = postgresql['spec']['enableConnectionPooler']
|
||||||
|
if not cp:
|
||||||
|
if 'enableConnectionPooler' in o['spec']:
|
||||||
|
del o['spec']['enableConnectionPooler']
|
||||||
|
else:
|
||||||
|
spec['enableConnectionPooler'] = True
|
||||||
|
else:
|
||||||
|
if 'enableConnectionPooler' in o['spec']:
|
||||||
|
del o['spec']['enableConnectionPooler']
|
||||||
|
|
||||||
if 'enableReplicaLoadBalancer' in postgresql['spec']:
|
if 'enableReplicaLoadBalancer' in postgresql['spec']:
|
||||||
rlb = postgresql['spec']['enableReplicaLoadBalancer']
|
rlb = postgresql['spec']['enableReplicaLoadBalancer']
|
||||||
if not rlb:
|
if not rlb:
|
||||||
|
|
@ -1006,7 +1036,7 @@ def init_cluster():
|
||||||
def main(port, secret_key, debug, clusters: list):
|
def main(port, secret_key, debug, clusters: list):
|
||||||
global TARGET_NAMESPACE
|
global TARGET_NAMESPACE
|
||||||
|
|
||||||
basicConfig(level=DEBUG if debug else INFO)
|
basicConfig(stream=sys.stdout, level=(DEBUG if debug else INFO), format='%(asctime)s %(levelname)s: %(message)s',)
|
||||||
|
|
||||||
init_cluster()
|
init_cluster()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from boto3 import client
|
from boto3 import client
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from furl import furl
|
from furl import furl
|
||||||
from json import dumps
|
from json import dumps, loads
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from os import environ, getenv
|
from os import environ, getenv
|
||||||
from requests import Session
|
from requests import Session
|
||||||
|
|
@ -18,6 +18,15 @@ session = Session()
|
||||||
|
|
||||||
OPERATOR_CLUSTER_NAME_LABEL = getenv('OPERATOR_CLUSTER_NAME_LABEL', 'cluster-name')
|
OPERATOR_CLUSTER_NAME_LABEL = getenv('OPERATOR_CLUSTER_NAME_LABEL', 'cluster-name')
|
||||||
|
|
||||||
|
COMMON_CLUSTER_LABEL = getenv('COMMON_CLUSTER_LABEL', '{"application":"spilo"}')
|
||||||
|
COMMON_POOLER_LABEL = getenv('COMMONG_POOLER_LABEL', '{"application":"db-connection-pooler"}')
|
||||||
|
|
||||||
|
logger.info("Common Cluster Label: {}".format(COMMON_CLUSTER_LABEL))
|
||||||
|
logger.info("Common Pooler Label: {}".format(COMMON_POOLER_LABEL))
|
||||||
|
|
||||||
|
COMMON_CLUSTER_LABEL = loads(COMMON_CLUSTER_LABEL)
|
||||||
|
COMMON_POOLER_LABEL = loads(COMMON_POOLER_LABEL)
|
||||||
|
|
||||||
|
|
||||||
def request(cluster, path, **kwargs):
|
def request(cluster, path, **kwargs):
|
||||||
if 'timeout' not in kwargs:
|
if 'timeout' not in kwargs:
|
||||||
|
|
@ -85,6 +94,7 @@ def resource_api_version(resource_type):
|
||||||
return {
|
return {
|
||||||
'postgresqls': 'apis/acid.zalan.do/v1',
|
'postgresqls': 'apis/acid.zalan.do/v1',
|
||||||
'statefulsets': 'apis/apps/v1',
|
'statefulsets': 'apis/apps/v1',
|
||||||
|
'deployments': 'apis/apps/v1',
|
||||||
}.get(resource_type, 'api/v1')
|
}.get(resource_type, 'api/v1')
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -149,7 +159,7 @@ def read_pod(cluster, namespace, resource_name):
|
||||||
resource_type='pods',
|
resource_type='pods',
|
||||||
namespace=namespace,
|
namespace=namespace,
|
||||||
resource_name=resource_name,
|
resource_name=resource_name,
|
||||||
label_selector={'application': 'spilo'},
|
label_selector=COMMON_CLUSTER_LABEL,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -159,7 +169,17 @@ def read_service(cluster, namespace, resource_name):
|
||||||
resource_type='services',
|
resource_type='services',
|
||||||
namespace=namespace,
|
namespace=namespace,
|
||||||
resource_name=resource_name,
|
resource_name=resource_name,
|
||||||
label_selector={'application': 'spilo'},
|
label_selector=COMMON_CLUSTER_LABEL,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def read_pooler(cluster, namespace, resource_name):
|
||||||
|
return kubernetes_get(
|
||||||
|
cluster=cluster,
|
||||||
|
resource_type='deployments',
|
||||||
|
namespace=namespace,
|
||||||
|
resource_name=resource_name,
|
||||||
|
label_selector=COMMON_POOLER_LABEL,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -169,7 +189,7 @@ def read_statefulset(cluster, namespace, resource_name):
|
||||||
resource_type='statefulsets',
|
resource_type='statefulsets',
|
||||||
namespace=namespace,
|
namespace=namespace,
|
||||||
resource_name=resource_name,
|
resource_name=resource_name,
|
||||||
label_selector={'application': 'spilo'},
|
label_selector=COMMON_CLUSTER_LABEL,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
Flask-OAuthlib==0.9.5
|
Flask-OAuthlib==0.9.5
|
||||||
Flask==1.1.1
|
Flask==1.1.2
|
||||||
backoff==1.8.1
|
backoff==1.8.1
|
||||||
boto3==1.10.4
|
boto3==1.10.4
|
||||||
boto==2.49.0
|
boto==2.49.0
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
#!/bin/bash
|
||||||
|
/usr/bin/python3 -m operator_ui
|
||||||
Loading…
Reference in New Issue