nuvolaris/ferretdb.py (149 lines of code) (raw):
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
#
# Deploys a standalone ferretdb relying on postgres db
#
import kopf, json, time
import nuvolaris.kube as kube
import nuvolaris.kustomize as kus
import nuvolaris.config as cfg
import nuvolaris.util as util
import nuvolaris.postgres_operator as postgres
import logging
import nuvolaris.openwhisk as openwhisk
import urllib.parse
import nuvolaris.operator_util as operator_util
from nuvolaris.user_config import UserConfig
from nuvolaris.user_metadata import UserMetadata
def enrich_ferretdb_data(data):
data['size']= cfg.get('mongodb.volume-size') or 10
data['dir']= "/state"
data['storageClass']=cfg.get("nuvolaris.storageclass")
data['name']="nuvolaris-mongodb"
data['container']="ferretdb"
data['applypodsecurity']=util.get_enable_pod_security()
# use nuvolaris postgres db as default configuration for FERRETDB
data['ferretdb_postgres_url'] = postgres.get_base_postgres_url(data)
util.ferretb_affinity_tolerations_data(data)
def create(owner=None):
"""
Deploys ferret db and wait for the pod to be ready.
"""
logging.info("*** creating ferretdb")
data = util.get_postgres_config_data()
enrich_ferretdb_data(data)
tplp = ["security-set-attach.yaml","set-attach.yaml","ferretdb-sts.yaml"]
if(data['affinity'] or data['tolerations']):
tplp.append("affinity-tolerance-sts-core-attach.yaml")
mkust = kus.patchTemplates("ferretdb", tplp, data)
mspec = kus.kustom_list("ferretdb", mkust, templates=[], data=data)
if owner:
kopf.append_owner_reference(mspec['items'], owner)
else:
cfg.put("state.ferretdb.spec", mspec)
res = kube.apply(mspec)
# dynamically detect ferretdb pod and wait for readiness
if res:
util.wait_for_pod_ready("{.items[?(@.metadata.labels.app == 'nuvolaris-mongodb')].metadata.name}")
update_system_cm_for_mdb(data)
logging.info("*** created ferretdb")
return res
def get_ferret_pod_name():
return util.get_pod_name("{.items[?(@.metadata.labels.app == 'nuvolaris-mongodb')].metadata.name}")
def update_system_cm_for_mdb(data):
logging.info("*** annotating configuration for ferretdb nuvolaris user")
try:
mdb_service = util.get_service("{.items[?(@.metadata.name == 'nuvolaris-mongodb-svc')]}")
if(mdb_service):
mdb_pod_name = get_ferret_pod_name()
mdb_service_name = mdb_service['metadata']['name']
mdb_ns = mdb_service['metadata']['namespace']
data = util.get_postgres_config_data()
username = urllib.parse.quote(data['postgres_nuvolaris_user'])
password = urllib.parse.quote(data['postgres_nuvolaris_password'])
auth = f"{username}:{password}"
mdb_url = f"mongodb://{auth}@{mdb_pod_name}.{mdb_service_name}.{mdb_ns}.svc.cluster.local:27017/nuvolaris?connectTimeoutMS=60000&authMechanism=PLAIN"
openwhisk.annotate(f"mongodb_url={mdb_url}")
logging.info("*** saved annotation for ferretdb nuvolaris user")
except Exception as e:
logging.error(f"failed to build mongodb_url for nuvolaris database: {e}")
def delete_by_owner():
spec = kus.build("ferretdb")
res = kube.delete(spec)
logging.info(f"delete ferretdb: {res}")
return res
def delete_by_spec():
spec = cfg.get("state.ferretdb.spec")
res = False
if spec:
res = kube.delete(spec)
logging.info(f"delete ferretdb: {res}")
return res
def delete(owner=None):
if owner:
return delete_by_owner()
else:
return delete_by_spec()
def patch(status, action, owner=None):
"""
Called the the operator patcher to create/delete ferredb
"""
try:
logging.info(f"*** handling request to {action} ferretdb")
if action == 'create':
msg = create(owner)
operator_util.patch_operator_status(status,'ferretdb','on')
else:
msg = delete(owner)
operator_util.patch_operator_status(status,'ferretdb','off')
logging.info(msg)
logging.info(f"*** handled request to {action} ferretdb")
except Exception as e:
logging.error('*** failed to update ferretdb: %s' % e)
operator_util.patch_operator_status(status,'ferretdb','error')
def _add_mdb_user_metadata(user_metadata, data):
"""
adds an entry for the mongodb connectivity, i.e
something like "mongodb://{namespace}:{auth}@nuvolaris-mongodb-0.nuvolaris-mongodb-svc.nuvolaris.svc.cluster.local:27017/{database}?connectTimeoutMS=60000"}
"""
try:
mdb_service = util.get_service("{.items[?(@.metadata.name == 'nuvolaris-mongodb-svc')]}")
if(mdb_service):
mdb_service_name = mdb_service['metadata']['name']
mdb_ns = mdb_service['metadata']['namespace']
mdb_pod_name = get_ferret_pod_name()
username = urllib.parse.quote(data["username"])
password = urllib.parse.quote(data["password"])
auth = f"{username}:{password}"
database = data["database"]
mdb_url = f"mongodb://{auth}@{mdb_pod_name}.{mdb_service_name}.{mdb_ns}.svc.cluster.local:27017/{database}?connectTimeoutMS=60000&authMechanism=PLAIN"
user_metadata.add_metadata("MONGODB_URL",mdb_url)
return None
except Exception as e:
logging.error(f"failed to build mongodb_url for {data['database']}: {e}")
return None
def create_db_user(ucfg: UserConfig, user_metadata: UserMetadata):
database = ucfg.get('mongodb.database')
subject = ucfg.get('namespace')
namespace = ucfg.get('namespace')
logging.info(f"authorizing new ferretdb database {database}")
try:
data = util.get_postgres_config_data()
data["database"]=f"{database}_ferretdb"
data["username"]=f"{subject}_ferretdb"
data["password"]=ucfg.get('mongodb.password')
data["mode"]="create"
path_to_pgpass = postgres.render_postgres_script(f"{namespace}_ferretdb","pgpass_tpl.properties",data)
path_to_mdb_script = postgres.render_postgres_script(f"{namespace}_ferretdb","postgres_manage_user_tpl.sql",data)
pod_name = util.get_pod_name("{.items[?(@.metadata.labels.app == 'nuvolaris-postgres')].metadata.name}")
if(pod_name):
res = postgres.exec_psql_command(pod_name,path_to_mdb_script,path_to_pgpass)
if res:
_add_mdb_user_metadata(user_metadata, data)
return res
else:
logging.error("failed to execute SQL script on Postgres DB to enable MongoDB (FerretDB) user")
return None
except Exception as e:
logging.error(f"failed to add Mongodb database {database}: {e}")
return None
def delete_db_user(namespace, database):
logging.info(f"removing ferretdb database {database}")
try:
data = util.get_postgres_config_data()
data["database"]=f"{database}_ferretdb"
data["username"]=f"{namespace}_ferretdb"
data["mode"]="delete"
path_to_pgpass = postgres.render_postgres_script(f"{namespace}_ferretdb","pgpass_tpl.properties",data)
path_to_mdb_script = postgres.render_postgres_script(f"{namespace}_ferretdb","postgres_manage_user_tpl.sql",data)
pod_name = util.get_pod_name("{.items[?(@.metadata.labels.app == 'nuvolaris-postgres')].metadata.name}")
if(pod_name):
res = postgres.exec_psql_command(pod_name,path_to_mdb_script,path_to_pgpass)
return res
return None
except Exception as e:
logging.error(f"failed to remove Ferretdb database {namespace} authorization id and key: {e}")
return None