diff --git a/deploy/all.sh b/deploy/all.sh index c6da23366d3cd74b63fde87c3f24960c3bc2999b..254cc40c9275b5fc2c89c785a1838dac26d73ba7 100755 --- a/deploy/all.sh +++ b/deploy/all.sh @@ -98,6 +98,29 @@ export NATS_SECRET_NAMESPACE=${NATS_SECRET_NAMESPACE:-${TFS_K8S_NAMESPACE}} # If NATS_REDEPLOY is "YES", the message broker will be dropped while checking/deploying NATS. export NATS_REDEPLOY=${NATS_REDEPLOY:-""} +# If not already set, set the namespace where QuestDB will be deployed. +export QDB_NAMESPACE=${QDB_NAMESPACE:-"qdb"} + +# If not already set, set the database username to be used by Monitoring. +export QDB_USERNAME=${QDB_USERNAME:-"admin"} + +# If not already set, set the database user's password to be used by Monitoring. +export QDB_PASSWORD=${QDB_PASSWORD:-"quest"} + +# If not already set, set the table name to be used by Monitoring. +export QDB_TABLE=${QDB_TABLE:-"tfs_monitoring"} + +## If not already set, disable flag for dropping table if exists. +## WARNING: ACTIVATING THIS FLAG IMPLIES LOOSING THE TABLE INFORMATION! +## If QDB_DROP_TABLE_IF_EXISTS is "YES", the table pointed by variable QDB_TABLE will be dropped while +## checking/deploying QuestDB. +#export QDB_DROP_TABLE_IF_EXISTS=${QDB_DROP_TABLE_IF_EXISTS:-""} + +# If not already set, disable flag for re-deploying QuestDB from scratch. +# WARNING: ACTIVATING THIS FLAG IMPLIES LOOSING THE DATABASE INFORMATION! +# If QDB_REDEPLOY is "YES", the database will be dropped while checking/deploying QuestDB. +export QDB_REDEPLOY=${QDB_REDEPLOY:-"YES"} + ######################################################################################################################## # Automated steps start here @@ -109,6 +132,9 @@ export NATS_REDEPLOY=${NATS_REDEPLOY:-""} # Deploy NATS ./deploy/nats.sh +# Deploy QuestDB +./deploy/qdb.sh + # Deploy TFS ./deploy/tfs.sh diff --git a/deploy/crdb.sh b/deploy/crdb.sh index 598980ac84a3b49e55402993e22ed963e80b2e38..90456773b615e9914f601133cb7da949811e06ff 100755 --- a/deploy/crdb.sh +++ b/deploy/crdb.sh @@ -44,7 +44,7 @@ export CRDB_DEPLOY_MODE=${CRDB_DEPLOY_MODE:-"single"} # If not already set, disable flag for dropping database if exists. # WARNING: ACTIVATING THIS FLAG IMPLIES LOOSING THE DATABASE INFORMATION! -# If CRDB_DROP_DATABASE_IF_EXISTS is "YES", the database pointed by variable CRDB_NAMESPACE will be dropped while +# If CRDB_DROP_DATABASE_IF_EXISTS is "YES", the database pointed by variable CRDB_DATABASE will be dropped while # checking/deploying CockroachDB. export CRDB_DROP_DATABASE_IF_EXISTS=${CRDB_DROP_DATABASE_IF_EXISTS:-""} diff --git a/deploy/qdb.sh b/deploy/qdb.sh new file mode 100755 index 0000000000000000000000000000000000000000..1e9d4d8ee264032a6e0073223e1363f7bf3d7910 --- /dev/null +++ b/deploy/qdb.sh @@ -0,0 +1,165 @@ +#!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed 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. + + +######################################################################################################################## +# Read deployment settings +######################################################################################################################## + +# If not already set, set the namespace where QuestDB will be deployed. +export QDB_NAMESPACE=${QDB_NAMESPACE:-"qdb"} + +# If not already set, set the database username to be used by Monitoring. +export QDB_USERNAME=${QDB_USERNAME:-"admin"} + +# If not already set, set the database user's password to be used by Monitoring. +export QDB_PASSWORD=${QDB_PASSWORD:-"quest"} + +# If not already set, set the table name to be used by Monitoring. +export QDB_TABLE=${QDB_TABLE:-"tfs_monitoring"} + +## If not already set, disable flag for dropping table if exists. +## WARNING: ACTIVATING THIS FLAG IMPLIES LOOSING THE TABLE INFORMATION! +## If QDB_DROP_TABLE_IF_EXISTS is "YES", the table pointed by variable QDB_TABLE will be dropped while +## checking/deploying QuestDB. +#export QDB_DROP_TABLE_IF_EXISTS=${QDB_DROP_TABLE_IF_EXISTS:-""} + +# If not already set, disable flag for re-deploying QuestDB from scratch. +# WARNING: ACTIVATING THIS FLAG IMPLIES LOOSING THE DATABASE INFORMATION! +# If QDB_REDEPLOY is "YES", the database will be dropped while checking/deploying QuestDB. +export QDB_REDEPLOY=${QDB_REDEPLOY:-""} + + +######################################################################################################################## +# Automated steps start here +######################################################################################################################## + +# Constants +TMP_FOLDER="./tmp" +QDB_MANIFESTS_PATH="manifests/questdb" + +# Create a tmp folder for files modified during the deployment +TMP_MANIFESTS_FOLDER="$TMP_FOLDER/manifests" +TMP_LOGS_FOLDER="$TMP_FOLDER/logs" +QDB_LOG_FILE="$TMP_LOGS_FOLDER/qdb_deploy.log" +mkdir -p $TMP_LOGS_FOLDER + +function qdb_deploy() { + echo "QuestDB Namespace" + echo ">>> Create QuestDB Namespace (if missing)" + kubectl create namespace ${QDB_NAMESPACE} + echo + + echo "QuestDB" + echo ">>> Checking if QuestDB is deployed..." + if kubectl get --namespace ${QDB_NAMESPACE} statefulset/questdb &> /dev/null; then + echo ">>> QuestDB is present; skipping step." + else + echo ">>> Deploy QuestDB" + cp "${QDB_MANIFESTS_PATH}/manifest.yaml" "${TMP_MANIFESTS_FOLDER}/qdb_manifest.yaml" + kubectl apply --namespace ${QDB_NAMESPACE} -f "${TMP_MANIFESTS_FOLDER}/qdb_manifest.yaml" + + echo ">>> Waiting QuestDB statefulset to be created..." + while ! kubectl get --namespace ${QDB_NAMESPACE} statefulset/questdb &> /dev/null; do + printf "%c" "." + sleep 1 + done + + # Wait for statefulset condition "Available=True" does not work + # Wait for statefulset condition "jsonpath='{.status.readyReplicas}'=3" throws error: + # "error: readyReplicas is not found" + # Workaround: Check the pods are ready + #echo ">>> QuestDB statefulset created. Waiting for readiness condition..." + #kubectl wait --namespace ${QDB_NAMESPACE} --for=condition=Available=True --timeout=300s statefulset/questdb + #kubectl wait --namespace ${QDB_NAMESPACE} --for=jsonpath='{.status.readyReplicas}'=3 --timeout=300s \ + # statefulset/questdb + echo ">>> QuestDB statefulset created. Waiting QuestDB pods to be created..." + while ! kubectl get --namespace ${QDB_NAMESPACE} pod/questdb-0 &> /dev/null; do + printf "%c" "." + sleep 1 + done + kubectl wait --namespace ${QDB_NAMESPACE} --for=condition=Ready --timeout=300s pod/questdb-0 + fi + echo + + echo "QuestDB Port Mapping" + echo ">>> Expose QuestDB SQL port (8812->8812)" + QDB_SQL_PORT=$(kubectl --namespace ${QDB_NAMESPACE} get service questdb-public -o 'jsonpath={.spec.ports[?(@.name=="sql")].port}') + PATCH='{"data": {"'${QDB_SQL_PORT}'": "'${QDB_NAMESPACE}'/questdb-public:'${QDB_SQL_PORT}'"}}' + kubectl patch configmap nginx-ingress-tcp-microk8s-conf --namespace ingress --patch "${PATCH}" + + PORT_MAP='{"containerPort": '${QDB_SQL_PORT}', "hostPort": '${QDB_SQL_PORT}'}' + CONTAINER='{"name": "nginx-ingress-microk8s", "ports": ['${PORT_MAP}']}' + PATCH='{"spec": {"template": {"spec": {"containers": ['${CONTAINER}']}}}}' + kubectl patch daemonset nginx-ingress-microk8s-controller --namespace ingress --patch "${PATCH}" + echo + + echo ">>> Expose QuestDB Influx Line Protocol port (9009->9009)" + QDB_ILP_PORT=$(kubectl --namespace ${QDB_NAMESPACE} get service questdb-public -o 'jsonpath={.spec.ports[?(@.name=="ilp")].port}') + PATCH='{"data": {"'${QDB_ILP_PORT}'": "'${QDB_NAMESPACE}'/questdb-public:'${QDB_ILP_PORT}'"}}' + kubectl patch configmap nginx-ingress-tcp-microk8s-conf --namespace ingress --patch "${PATCH}" + + PORT_MAP='{"containerPort": '${QDB_ILP_PORT}', "hostPort": '${QDB_ILP_PORT}'}' + CONTAINER='{"name": "nginx-ingress-microk8s", "ports": ['${PORT_MAP}']}' + PATCH='{"spec": {"template": {"spec": {"containers": ['${CONTAINER}']}}}}' + kubectl patch daemonset nginx-ingress-microk8s-controller --namespace ingress --patch "${PATCH}" + echo + + echo ">>> Expose QuestDB HTTP Mgmt GUI port (9000->9000)" + QDB_GUI_PORT=$(kubectl --namespace ${QDB_NAMESPACE} get service questdb-public -o 'jsonpath={.spec.ports[?(@.name=="http")].port}') + PATCH='{"data": {"'${QDB_GUI_PORT}'": "'${QDB_NAMESPACE}'/questdb-public:'${QDB_GUI_PORT}'"}}' + kubectl patch configmap nginx-ingress-tcp-microk8s-conf --namespace ingress --patch "${PATCH}" + + PORT_MAP='{"containerPort": '${QDB_GUI_PORT}', "hostPort": '${QDB_GUI_PORT}'}' + CONTAINER='{"name": "nginx-ingress-microk8s", "ports": ['${PORT_MAP}']}' + PATCH='{"spec": {"template": {"spec": {"containers": ['${CONTAINER}']}}}}' + kubectl patch daemonset nginx-ingress-microk8s-controller --namespace ingress --patch "${PATCH}" + echo +} + +function qdb_undeploy() { + echo "QuestDB" + echo ">>> Checking if QuestDB is deployed..." + if kubectl get --namespace ${QDB_NAMESPACE} statefulset/questdb &> /dev/null; then + echo ">>> Undeploy QuestDB" + kubectl delete --namespace ${QDB_NAMESPACE} -f "${TMP_MANIFESTS_FOLDER}/qdb_manifest.yaml" --ignore-not-found + else + echo ">>> QuestDB is not present; skipping step." + fi + echo + + echo "QuestDB Namespace" + echo ">>> Delete QuestDB Namespace (if exists)" + echo "NOTE: this step might take few minutes to complete!" + kubectl delete namespace ${QDB_NAMESPACE} --ignore-not-found + echo +} + +# TODO: implement method to drop table +#function qdb_drop_table() { +# echo "Drop table if exists" +# QDB_CLIENT_URL="postgresql://${QDB_USERNAME}:${QDB_PASSWORD}@questdb-0:${QDB_SQL_PORT}/defaultdb?sslmode=require" +# kubectl exec -it --namespace ${QDB_NAMESPACE} questdb-0 -- \ +# ./qdb sql --certs-dir=/qdb/qdb-certs --url=${QDB_CLIENT_URL} \ +# --execute "DROP TABLE IF EXISTS ${QDB_TABLE};" +# echo +#} + +if [ "$QDB_REDEPLOY" == "YES" ]; then + qdb_undeploy +#elif [ "$QDB_DROP_TABLE_IF_EXISTS" == "YES" ]; then +# qdb_drop_table +fi +qdb_deploy diff --git a/deploy/tfs.sh b/deploy/tfs.sh index 86043ee44829904786e700df813400476ca4e755..34ddb3903e65ccd8f32fe9428aea91c8f6a05f18 100755 --- a/deploy/tfs.sh +++ b/deploy/tfs.sh @@ -57,6 +57,18 @@ export CRDB_DATABASE=${CRDB_DATABASE:-"tfs"} # If not already set, set the namespace where NATS will be deployed. export NATS_NAMESPACE=${NATS_NAMESPACE:-"nats"} +# If not already set, set the namespace where QuestDB will be deployed. +export QDB_NAMESPACE=${QDB_NAMESPACE:-"qdb"} + +# If not already set, set the database username to be used by Monitoring. +export QDB_USERNAME=${QDB_USERNAME:-"admin"} + +# If not already set, set the database user's password to be used by Monitoring. +export QDB_PASSWORD=${QDB_PASSWORD:-"quest"} + +# If not already set, set the table name to be used by Monitoring. +export QDB_TABLE=${QDB_TABLE:-"tfs_monitoring"} + ######################################################################################################################## # Automated steps start here @@ -95,6 +107,22 @@ kubectl create secret generic nats-data --namespace ${TFS_K8S_NAMESPACE} --type= --from-literal=NATS_CLIENT_PORT=${NATS_CLIENT_PORT} printf "\n" +echo "Create secret with QuestDB data" +QDB_HTTP_PORT=$(kubectl --namespace ${QDB_NAMESPACE} get service questdb-public -o 'jsonpath={.spec.ports[?(@.name=="http")].port}') +QDB_ILP_PORT=$(kubectl --namespace ${QDB_NAMESPACE} get service questdb-public -o 'jsonpath={.spec.ports[?(@.name=="ilp")].port}') +QDB_SQL_PORT=$(kubectl --namespace ${QDB_NAMESPACE} get service questdb-public -o 'jsonpath={.spec.ports[?(@.name=="sql")].port}') +METRICSDB_HOSTNAME="questdb-public.${QDB_NAMESPACE}.svc.cluster.local" +kubectl create secret generic qdb-data --namespace ${TFS_K8S_NAMESPACE} --type='Opaque' \ + --from-literal=QDB_NAMESPACE=${QDB_NAMESPACE} \ + --from-literal=METRICSDB_HOSTNAME=${METRICSDB_HOSTNAME} \ + --from-literal=METRICSDB_REST_PORT=${QDB_HTTP_PORT} \ + --from-literal=METRICSDB_ILP_PORT=${QDB_ILP_PORT} \ + --from-literal=METRICSDB_SQL_PORT=${QDB_SQL_PORT} \ + --from-literal=METRICSDB_TABLE=${QDB_TABLE} \ + --from-literal=METRICSDB_USERNAME=${QDB_USERNAME} \ + --from-literal=METRICSDB_PASSWORD=${QDB_PASSWORD} +printf "\n" + echo "Deploying components and collecting environment variables..." ENV_VARS_SCRIPT=tfs_runtime_env_vars.sh echo "# Environment variables for TeraFlowSDN deployment" > $ENV_VARS_SCRIPT @@ -251,17 +279,6 @@ for EXTRA_MANIFEST in $TFS_EXTRA_MANIFESTS; do done printf "\n" -# By now, leave these controls here. Some component dependencies are not well handled. - -if [[ "$TFS_COMPONENTS" == *"monitoring"* ]]; then - echo "Waiting for 'MonitoringDB' component..." - # Kubernetes does not implement --for='condition=available' for statefulsets. - # By now, we assume a single replica of monitoringdb. To be updated in future releases. - kubectl wait --namespace $TFS_K8S_NAMESPACE \ - --for=jsonpath='{.status.readyReplicas}'=1 --timeout=300s statefulset/monitoringdb - printf "\n" -fi - for COMPONENT in $TFS_COMPONENTS; do echo "Waiting for '$COMPONENT' component..." COMPONENT_OBJNAME=$(echo "${COMPONENT}" | sed "s/\_/-/") @@ -274,34 +291,17 @@ if [[ "$TFS_COMPONENTS" == *"webui"* ]] && [[ "$TFS_COMPONENTS" == *"monitoring" echo "Configuring WebUI DataStores and Dashboards..." sleep 3 - # INFLUXDB_HOST="monitoringservice" - # INFLUXDB_PORT=$(kubectl --namespace $TFS_K8S_NAMESPACE get service/monitoringservice -o jsonpath='{.spec.ports[?(@.name=="influxdb")].port}') - # INFLUXDB_URL="http://${INFLUXDB_HOST}:${INFLUXDB_PORT}" - # INFLUXDB_USER=$(kubectl --namespace $TFS_K8S_NAMESPACE get secrets influxdb-secrets -o jsonpath='{.data.INFLUXDB_ADMIN_USER}' | base64 --decode) - # INFLUXDB_PASSWORD=$(kubectl --namespace $TFS_K8S_NAMESPACE get secrets influxdb-secrets -o jsonpath='{.data.INFLUXDB_ADMIN_PASSWORD}' | base64 --decode) - # INFLUXDB_DATABASE=$(kubectl --namespace $TFS_K8S_NAMESPACE get secrets influxdb-secrets -o jsonpath='{.data.INFLUXDB_DB}' | base64 --decode) - # Exposed through the ingress controller "tfs-ingress" - GRAFANA_HOSTNAME="127.0.0.1" - GRAFANA_PORT="80" - GRAFANA_BASEURL="/grafana" + GRAFANA_URL="127.0.0.1:80/grafana" # Default Grafana credentials GRAFANA_USERNAME="admin" GRAFANA_PASSWORD="admin" - # Default Grafana API URL - GRAFANA_URL_DEFAULT="http://${GRAFANA_USERNAME}:${GRAFANA_PASSWORD}@${GRAFANA_HOSTNAME}:${GRAFANA_PORT}${GRAFANA_BASEURL}" - - # Updated Grafana API URL - GRAFANA_URL_UPDATED="http://${GRAFANA_USERNAME}:${TFS_GRAFANA_PASSWORD}@${GRAFANA_HOSTNAME}:${GRAFANA_PORT}${GRAFANA_BASEURL}" - - echo "export GRAFANA_URL_UPDATED=${GRAFANA_URL_UPDATED}" >> $ENV_VARS_SCRIPT - - echo "Connecting to grafana at URL: ${GRAFANA_URL_DEFAULT}..." - # Configure Grafana Admin Password # Ref: https://grafana.com/docs/grafana/latest/http_api/user/#change-password + GRAFANA_URL_DEFAULT="http://${GRAFANA_USERNAME}:${GRAFANA_PASSWORD}@${GRAFANA_URL}" + echo "Connecting to grafana at URL: ${GRAFANA_URL_DEFAULT}..." curl -X PUT -H "Content-Type: application/json" -d '{ "oldPassword": "'${GRAFANA_PASSWORD}'", "newPassword": "'${TFS_GRAFANA_PASSWORD}'", @@ -309,16 +309,21 @@ if [[ "$TFS_COMPONENTS" == *"webui"* ]] && [[ "$TFS_COMPONENTS" == *"monitoring" }' ${GRAFANA_URL_DEFAULT}/api/user/password echo + # Updated Grafana API URL + GRAFANA_URL_UPDATED="http://${GRAFANA_USERNAME}:${TFS_GRAFANA_PASSWORD}@${GRAFANA_URL}" + echo "export GRAFANA_URL_UPDATED=${GRAFANA_URL_UPDATED}" >> $ENV_VARS_SCRIPT + # Ref: https://grafana.com/docs/grafana/latest/http_api/data_source/ # TODO: replace user, password and database by variables to be saved + QDB_HOST_PORT="${METRICSDB_HOSTNAME}:${QDB_SQL_PORT}" echo "Creating a datasource..." curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -d '{ "access" : "proxy", "type" : "postgres", - "name" : "monitoringdb", - "url" : "monitoringservice:8812", - "database" : "monitoring", - "user" : "admin", + "name" : "questdb", + "url" : "'${QDB_HOST_PORT}'", + "database" : "'${QDB_TABLE}'", + "user" : "'${QDB_USERNAME}'", "basicAuth": false, "isDefault": true, "jsonData" : { @@ -333,9 +338,7 @@ if [[ "$TFS_COMPONENTS" == *"webui"* ]] && [[ "$TFS_COMPONENTS" == *"monitoring" "tlsConfigurationMethod": "file-path", "tlsSkipVerify" : true }, - "secureJsonData": { - "password": "quest" - } + "secureJsonData": {"password": "'${QDB_PASSWORD}'"} }' ${GRAFANA_URL_UPDATED}/api/datasources echo @@ -352,4 +355,3 @@ if [[ "$TFS_COMPONENTS" == *"webui"* ]] && [[ "$TFS_COMPONENTS" == *"monitoring" printf "\n\n" fi -