Avatar

Scripting for better devops: a Python kata

← Back to list
Posted on 15.08.2023
Image by AI on Midjourney
Refill!

I like automating things, so I try cutting edges where possible. One of the tiny annoying troubles bugging me was figuring the right live database to connect to, or the right cluster to select with kubectl, while knowing only a country name. Surely, if someone has just 2-3 countries, or even regional deployment, this does not turn into a problem at all. But if the deployment has 30, 40, 70 countries?

So, I've decided to make a script that takes a country name or code as an input, and makes a connection to the right database or cluster.

At first, I wanted to write the script in NodeJS, but not every engineer has NodeJS runtime installed. This implies serious distribution difficulties, especially if the script becomes a part of a project repository. Bash scripting was also off the table, because I find this language quite cumbersome and hardly suitable for complex scripts.

I've decided to give Python a try. After all, Python is a language of many fields: system scripting, plugin development, cloud native development.

The script

So, behold! My first script written in Python... using with help of ChatGPT obviously.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
import subprocess
PROJECT_NAME = "my-project"
regions = {
"eu": "europe-west1",
"us": "us-east1",
}
clusters = {
"eu": "gke_my-project-live_europe-west1_01",
"us": "gke_my-project-live_us-east1_01",
}
countries = [
# Asia
{"name": "USA", "code": "us", "region": "us", "db_instance": "live-us"},
{"name": "Germany", "code": "de", "region": "eu", "db_instance": "live-de"},
]
def main():
args = parse_arguments()
country = find_country(args.country)
if country == None:
print("No such country: {}".format(args.country))
exit(1)
if args.action == "database":
if args.rw:
print("Caution: read-write mode enabled!")
connect_database(country, args.rw)
elif args.action == "cluster":
select_cluster(country)
exit(0)
def select_cluster(country):
kubectl_cmd = get_absolute_path("kubectl")
execute("{} config use-context {}".format(kubectl_cmd, clusters[country["region"]]))
def connect_database(country, rw):
if rw:
db_instance = country["db_instance"]
else:
db_instance = country.get("db_instance_replica", "{}-replica".format(country["db_instance"]))
db_region = country.get("db_region", country["region"])
cloud_sql_proxy_cmd = get_absolute_path("cloud_sql_proxy")
execute("{} -instances={}:{}:{}=tcp:54321".format(cloud_sql_proxy_cmd, PROJECT_NAME, regions[db_region], db_instance))
def execute(command):
print("Executing: {}".format(command))
try:
subprocess.run(command, shell=False)
except KeyboardInterrupt:
print("Ctrl+C pressed. Exiting gracefully.")
def get_absolute_path(cmd):
output = subprocess.run("which {}".format(cmd), capture_output=True, shell=True)
return output.stdout.decode()[:-1]
def find_country(country):
country_uppercased = country.upper()
for country_element in countries:
if (country_element["name"].upper() == country_uppercased or country_element["code"].upper() == country_uppercased):
return country_element
return None
def parse_arguments():
parser = argparse.ArgumentParser(description="A helper tool for faster devops")
parser.add_argument("country", type=str, help="The country name or code")
parser.add_argument("action", choices=["database", "cluster"], help="What to do")
parser.add_argument("--rw", action="store_true", help="If set to TRUE, the connection will be writable")
return parser.parse_args()
if __name__ == "__main__":
main()
The code is licensed under the MIT license

Well, this script has proven its usefulness to me, and hopefully it will do so too to whoever stumbles upon this tiny post :)


Avatar

Sergei Gannochenko

Business-oriented fullstack engineer, in ❤️ with Tech.
React, Node, Go, Docker, AWS, Jamstack.
15+ years in dev.