|
|
|
@ -0,0 +1,89 @@ |
|
|
|
|
#!/usr/bin/env python3 |
|
|
|
|
import asyncio |
|
|
|
|
import ecs_logging |
|
|
|
|
import logging |
|
|
|
|
import os |
|
|
|
|
import sys |
|
|
|
|
from kubernetes_asyncio.client.api_client import ApiClient |
|
|
|
|
from kubernetes_asyncio import client, config, watch |
|
|
|
|
from time import time |
|
|
|
|
|
|
|
|
|
# Get the Logger |
|
|
|
|
logger = logging.getLogger() |
|
|
|
|
logger.setLevel(logging.INFO) |
|
|
|
|
|
|
|
|
|
# Add an ECS formatter to the Handler |
|
|
|
|
handler = logging.StreamHandler() |
|
|
|
|
handler.setFormatter(ecs_logging.StdlibFormatter()) |
|
|
|
|
logger.addHandler(handler) |
|
|
|
|
|
|
|
|
|
ZONEFILE_TEMPLATE = """ |
|
|
|
|
@ IN SOA ns1.%(zone)s. hostmaster.%(zone)s. (2022060820 300 300 300 300) |
|
|
|
|
NS ns1.%(zone)s. |
|
|
|
|
ns1 A %(ip)s |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
_, ip = sys.argv |
|
|
|
|
|
|
|
|
|
PATH_CONFIG = "/var/bind/bind.conf" |
|
|
|
|
PATH_ZONEFILE = "/var/bind/db.%s" |
|
|
|
|
|
|
|
|
|
active_zones = set() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def update_config(): |
|
|
|
|
with open(PATH_CONFIG + ".part", "w") as fh: |
|
|
|
|
for zone in active_zones: |
|
|
|
|
path = PATH_ZONEFILE % zone |
|
|
|
|
fh.write("zone \"%s\" { type master; file \"%s\"; };\n" % (zone, path)) |
|
|
|
|
os.rename(PATH_CONFIG + ".part", PATH_CONFIG) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def update_loop(v1, apps_api, api_instance, revision): |
|
|
|
|
flt = "codemowers.io", "v1alpha1", "bindzones" |
|
|
|
|
method = api_instance.list_cluster_custom_object |
|
|
|
|
w = watch.Watch() |
|
|
|
|
latest_version = None |
|
|
|
|
logger.info("Subscribing to updates") |
|
|
|
|
async for event in w.stream(method, *flt, resource_version=latest_version): |
|
|
|
|
latest_version = event["object"]["metadata"]["resourceVersion"] |
|
|
|
|
if event["type"] == "ADDED": |
|
|
|
|
zone = event["object"]["metadata"]["name"] |
|
|
|
|
active_zones.add(zone) |
|
|
|
|
path = PATH_ZONEFILE % zone |
|
|
|
|
logger.info("Adding zone: %s", zone) |
|
|
|
|
if not os.path.exists(path): |
|
|
|
|
with open(path + ".part", "w") as fh: |
|
|
|
|
fh.write(ZONEFILE_TEMPLATE % { |
|
|
|
|
"ip": ip, |
|
|
|
|
"zone": zone |
|
|
|
|
}) |
|
|
|
|
os.rename(path + ".part", path) |
|
|
|
|
update_config() |
|
|
|
|
elif event["type"] == "DELETED": |
|
|
|
|
logger.info("Removing zone: %s", zone) |
|
|
|
|
active_zones.remove(zone) |
|
|
|
|
os.unlink(PATH_ZONEFILE % event["object"]["metadata"]["name"]) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def main(): |
|
|
|
|
if os.getenv("KUBECONFIG"): |
|
|
|
|
await config.load_kube_config() |
|
|
|
|
else: |
|
|
|
|
config.load_incluster_config() |
|
|
|
|
async with ApiClient() as api: |
|
|
|
|
v1 = client.CoreV1Api(api) |
|
|
|
|
apps_api = client.AppsV1Api() |
|
|
|
|
api_instance = client.CustomObjectsApi(api) |
|
|
|
|
revision = str(time()) |
|
|
|
|
while True: |
|
|
|
|
try: |
|
|
|
|
await update_loop(v1, apps_api, api_instance, revision) |
|
|
|
|
except asyncio.exceptions.TimeoutError: |
|
|
|
|
pass |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
|
loop = asyncio.new_event_loop() |
|
|
|
|
loop.run_until_complete(main()) |
|
|
|
|
loop.close() |