blob: 4a7ab1e82af69ddb449db3e5954aa30be584acf2 [file]
# 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.
#
import os, json, time, sys, logging
import requests as req
import openserverless.config as cfg
class CouchDB:
def __init__(self, environ=os.environ):
self._environ = environ
self.db_protocol = "http"
self.db_prefix = "nuvolaris_"
self.db_port = environ.get("COUCHDB_SERVICE_PORT", "5984")
self.db_host = environ.get("COUCHDB_SERVICE_HOST", "localhost")
self.db_username = environ.get("COUCHDB_ADMIN_USER", "whisk_admin")
self.db_password = environ.get("COUCHDB_ADMIN_PASSWORD", "wfoygT7dvDtE")
self.db_auth = req.auth.HTTPBasicAuth(self.db_username, self.db_password)
self.db_url = f"{self.db_protocol}://{self.db_host}:{self.db_port}"
self.db_base = f"{self.db_url}/{self.db_prefix}"
self.db_session = req.Session()
self.db_session.auth = self.db_auth
def wait_db_ready(self, max_seconds):
logging.info("entering CouchDB.wait_db_ready()")
start = time.time()
delta = 0
session = req.Session()
while delta < max_seconds:
try:
r = session.get(f"{self.db_url}/_utils", timeout=5)
logging.info(
f"CouchDB.wait_db_ready() got response code = {r.status_code}"
)
if r.status_code == 200:
return True
except Exception as e:
logging.info(f"waiting since: {delta} seconds")
delta = int(time.time() - start)
time.sleep(1)
return False
# check if database exists, return boolean
def check_db(self, database):
url = f"{self.db_base}{database}"
r = self.db_session.head(url)
return r.status_code == 200
# delete database, return true if ok
def delete_db(self, database):
url = f"{self.db_base}{database}"
r = self.db_session.delete(url)
return r.status_code == 200
# create db, return true if ok
def create_db(self, database):
url = f"{self.db_base}{database}"
r = self.db_session.put(url)
return r.status_code == 201
# database="subjects"
def recreate_db(self, database, recreate=False):
msg = "recreate_db:"
exists = self.check_db(database)
if recreate and exists:
msg += " deleted"
self.delete_db(database)
if recreate or not exists:
msg += " created"
self.create_db(database)
return msg
def get_doc(self, database, id, user=None, password="", no_auth=False):
url = f"{self.db_base}{database}/{id}"
session = req.Session()
if no_auth:
db_auth = None
elif user:
db_auth = req.auth.HTTPBasicAuth(user, password)
else:
db_auth = self.db_auth
session.auth = db_auth
r = session.get(url)
if r.status_code == 200:
return json.loads(r.text)
return None
def update_doc(self, database, doc):
if "_id" in doc:
url = f"{self.db_base}{database}/{doc['_id']}"
cur = self.get_doc(database, doc["_id"])
if cur and "_rev" in cur:
doc["_rev"] = cur["_rev"]
r = self.db_session.put(url, json=doc)
else:
r = self.db_session.put(url, json=doc)
return r.status_code in [200, 201]
return False
def delete_doc(self, database, id):
cur = self.get_doc(database, id)
if cur and "_rev" in cur:
url = f"{self.db_base}{database}/{cur['_id']}?rev={cur['_rev']}"
r = self.db_session.delete(url)
return r.status_code == 200
return False
def configure_single_node(self):
url = f"{self.db_url}/_cluster_setup"
data = {
"action": "enable_single_node",
"singlenode": True,
"bind_address": "0.0.0.0",
"port": 5984,
}
r = self.db_session.post(url, json=data)
return r.status_code == 201
def configure_no_reduce_limit(self):
url = f"{self.db_url}/_node/_local/_config/query_server_config/reduce_limit"
data = b'"false"'
r = self.db_session.put(url, data=data)
return r.status_code == 200
def add_user(self, username: str, password: str):
userpass = {"name": username, "password": password, "roles": [], "type": "user"}
url = f"{self.db_url}/_users/org.couchdb.user:{username}"
res = self.db_session.put(url, json=userpass)
return res.status_code in [200, 201, 421]
# def add_role(self, database: str, members: list[str] = [], admins: list[str] =[]):
def add_role(self, database: str, members=[], admins=[]):
roles = {
"admins": {"names": admins, "roles": []},
"members": {"names": members, "roles": []},
}
url = f"{self.db_base}{database}/_security"
res = self.db_session.put(url, json=roles)
return res.status_code in [200, 201, 421]
#
# Submit a POST request to the _find endpoint using the specified selector
#
def find_doc(self, database, selector, user=None, password="", no_auth=False):
url = f"{self.db_base}{database}/_find"
headers = {"Content-Type": "application/json"}
session = req.Session()
if no_auth:
db_auth = None
elif user:
db_auth = req.auth.HTTPBasicAuth(user, password)
else:
db_auth = self.db_auth
session.auth = db_auth
r = session.post(url, headers=headers, data=selector)
if r.status_code == 200:
return json.loads(r.text)
logging.warning(f"query to {url} failed with {r.status_code}. Body {r.text}")
return None