#!/usr/bin/env python3 import base64 import os from kubernetes import client, config from kubernetes.client.api_client import ApiClient from subprocess import Popen, PIPE from flask import Flask, request, send_file from urllib.parse import urlparse import logging import useful.logs useful.logs.setup(json_fields={"msg": "message", "level": "levelname"}) logger = logging.getLogger() TOKEN = os.environ["TOKEN"] app = Flask(__name__) if os.getenv("KUBECONFIG"): config.load_kube_config() else: config.load_incluster_config() def generate_targets(): with ApiClient() as api: v1 = client.CoreV1Api(api) api_instance = client.CustomObjectsApi(api) for i in v1.list_namespace().items: # Handle MySQL operator targets = api_instance.list_namespaced_custom_object( "mysql.oracle.com", "v2", i.metadata.name, "innodbclusters") for target in targets["items"]: yield "innodbclusters", i.metadata.name, "-", target["metadata"]["name"] # Handle MongoDB community operator targets = api_instance.list_namespaced_custom_object( "mongodbcommunity.mongodb.com", "v1", i.metadata.name, "mongodbcommunity") for target in targets["items"]: for user in target["spec"]["users"]: for role in user["roles"]: yield "mongodbcommunity", i.metadata.name, \ user["name"], role["db"] break @app.route("/cronjob.sh") def generate_script(): if request.headers.get("Authorization") != TOKEN: raise base = urlparse(request.base_url)._replace(path="", params="", query="", fragment="").geturl() def generate(): for z in generate_targets(): path = "/stream/%s/%s/%s/%s\n" % z yield "wget --content-disposition --header \"Authorization: %s\" %s%s" % (TOKEN, base, path) return app.response_class(generate(), mimetype="text/plain") @app.route("/stream/innodbclusters///") def stream_innodbclusters(namespace, user, database): if request.headers.get("Authorization") != TOKEN: raise with ApiClient() as api: v1 = client.CoreV1Api(api) secret_name = "%s-backup" % (database) secret = v1.read_namespaced_secret(secret_name, namespace) backup_username = base64.b64decode(secret.data["backupUsername"]).decode("ascii") backup_password = base64.b64decode(secret.data["backupPassword"]).decode("ascii") cmd = "/usr/bin/mysqldump", "--all-databases", "-u", backup_username, \ "--password=%s" % backup_password, \ "-h", "%s.%s.svc.cluster.local" % (database, namespace) logger.info("Executing: %s", " ".join(cmd)) process = Popen(cmd, stdout=PIPE, stdin=PIPE, close_fds=True, bufsize=4096 * 1024) gzip = Popen(["/usr/bin/gzip", "-9"], stdout=PIPE, stdin=process.stdout, close_fds=True, bufsize=4096 * 1024) return send_file(gzip.stdout, mimetype="application/gzip", as_attachment=True, download_name="%s_%s.sql.gz" % (namespace, database)) @app.route("/stream/mongodbcommunity///") def stream_mongodbcommunity(namespace, user, database): if request.headers.get("Authorization") != TOKEN: raise with ApiClient() as api: v1 = client.CoreV1Api(api) secret_name = "mongodb-%s-%s" % (database, user) secret = v1.read_namespaced_secret(secret_name, namespace) uri = base64.b64decode(secret.data["connectionString.standard"]).decode("ascii") cmd = "/usr/bin/mongodump", "--uri", uri, "--gzip", "--archive", "--quiet" logger.info("Executing: %s", " ".join(cmd)) process = Popen(cmd, stdout=PIPE, stdin=PIPE, close_fds=True, bufsize=4096 * 1024) return send_file(process.stdout, mimetype="application/tar+gzip", as_attachment=True, download_name="%s_%s.gz" % (namespace, database)) app.run(host="0.0.0.0", debug=False, threaded=True)