├── Dockerfile ├── LICENSE ├── README.md ├── app ├── app.py ├── requirements.txt ├── static │ ├── package-lock.json │ ├── package.json │ ├── prism.css │ ├── prism.js │ └── user-tie.svg ├── templates │ ├── base.html │ ├── footer.html │ ├── hostnames.html │ └── message.html └── uwsgi.ini ├── manifests ├── deployment.yaml ├── ingress.yaml ├── kustomization.yaml ├── rbac.yaml └── service.yaml ├── renovate.json ├── screenshots └── external-dns-ui_1.jpg └── test ├── kustomization.yaml ├── patch_external-dns-ui.yaml ├── setup_test_environment.sh ├── test_external-dns.yaml └── test_ingresses.yaml /Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020 SIGHUP s.r.l All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | FROM node:lts-alpine AS node 5 | COPY app/static /app 6 | WORKDIR /app 7 | RUN npm install 8 | 9 | 10 | FROM tiangolo/uwsgi-nginx-flask:python3.8-alpine 11 | RUN apk --update --no-cache add gcc=9.3.0-r0 linux-headers=4.19.36-r0 musl-dev=1.1.24-r3 libffi-dev=3.2.1-r6 libressl-dev=3.0.2-r0 bind-tools=9.14.12-r0 12 | ENV STATIC_URL /static 13 | ENV STATIC_PATH /app/static 14 | ENV LISTEN_PORT 8080 15 | EXPOSE 8080 16 | COPY ./app/requirements.txt /var/www/requirements.txt 17 | RUN pip install -r /var/www/requirements.txt 18 | COPY ./app /app 19 | COPY --from=node /app/node_modules /app/static/node_modules 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, SIGHUP 4 | All rights reserved. 5 | Project external-dns-ui was inspired by and based on SIGHUP's Gatekeeper Policy Manager. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | * Neither the name of the copyright holder nor the names of its 18 | contributors may be used to endorse or promote products derived from 19 | this software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 25 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # external-dns-ui 2 | 3 | External-dns-ui is a read-only web-based UI for external-dns (https://github.com/kubernetes-sigs/external-dns). 4 | 5 | The purpose of the project is to provide a quick overview of the creation status of Ingress DNS records via a correctly configured external-dns. 6 | 7 | ![screenshot](screenshots/external-dns-ui_1.jpg) 8 | 9 | ⚠️⚠️⚠️ This project is in alpha stage, use at your own risk ⚠️⚠️⚠️ 10 | 11 | :heart: Inspired by and based on Gatekeeper Policy Manager https://github.com/sighupio/gatekeeper-policy-manager :heart: 12 | 13 | ## Requirements 14 | 15 | - Kubernetes cluster 16 | - Correctly configured deployment of `external-dns` 17 | 18 | ## How to deploy external-dns-ui 19 | 20 | Simply apply the Kubernetes manifests generated via `manifests/kustomization.yaml` into your Kubernetes cluster. For example: 21 | 22 | ```bash 23 | kubectl apply -k manifests 24 | ``` 25 | 26 | ⚠️ Please ensure that the namespace specified inside `manifests/kustomization.yaml` is the namespace where your external-dns is running. 27 | 28 | Once you've deployed external-dns-ui, if you haven't set up an ingress, you can access the web-UI using port-forward: 29 | 30 | ```bash 31 | kubectl -n external-dns port-forward svc/external-dns-ui 8000:80 32 | ``` 33 | 34 | ## Configuration 35 | 36 | You can configure external-dns-ui via the following environment variables: 37 | 38 | | Env Var Name | Description | Default | 39 | | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ---------------------- | 40 | | `EDUI_NAMESPACE` | The name of the namespace where your external-dns deployment is running | `external-dns` | 41 | | `EDUI_PREFERRED_URL_SCHEME` | URL scheme to be used while generating links. | `http` | 42 | | `EDUI_LOG_LEVEL` | Log level (see [python logging docs](https://docs.python.org/2/library/logging.html#levels) for available levels) | `INFO` | 43 | 44 | ## Development 45 | 46 | External-dns-ui is written in Python using the Flask framework for the backend and Fromantic-UI for the frontend. 47 | 48 | Script `test/setup_test_environment.sh` builds & pushes the image, and runs the app in a Minikube test environment. 49 | -------------------------------------------------------------------------------- /app/app.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020 SIGHUP s.r.l All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | import os 6 | from functools import wraps 7 | from logging.config import dictConfig 8 | 9 | from flask import Flask, render_template 10 | from kubernetes import client, config 11 | from kubernetes.client.rest import ApiException 12 | from kubernetes.config.config_exception import ConfigException 13 | from urllib3.exceptions import MaxRetryError, NewConnectionError 14 | 15 | from urllib.parse import urljoin 16 | 17 | # Set up logging 18 | dictConfig( 19 | { 20 | "version": 1, 21 | "formatters": { 22 | "default": {"format": "[%(asctime)s] %(levelname)s: %(message)s"} 23 | }, 24 | "handlers": { 25 | "wsgi": { 26 | "class": "logging.StreamHandler", 27 | "stream": "ext://flask.logging.wsgi_errors_stream", 28 | "formatter": "default", 29 | } 30 | }, 31 | "root": { 32 | "level": os.environ.get("EDUI_LOG_LEVEL", "INFO"), 33 | "handlers": ["wsgi"], 34 | }, 35 | } 36 | ) 37 | 38 | app = Flask(__name__) 39 | 40 | # Update app config with env vars 41 | app.config.update( 42 | { 43 | "PREFERRED_URL_SCHEME": os.environ.get("EDUI_PREFERRED_URL_SCHEME", "http"), 44 | "EXTERNAL_DNS_NAMESPACE": os.environ.get("EDUI_NAMESPACE", "external-dns"), 45 | } 46 | ) 47 | 48 | def dict_to_li(my_dict, html): 49 | """Recursive function to convert dict items into
  • html tags""" 50 | if my_dict is None: 51 | return html 52 | for k, v in my_dict.items(): 53 | app.logger.debug("Processing %s, %s" % (k, v)) 54 | if not isinstance(v, dict): 55 | html += "
  • %s: %s
  • " % (k, v) 56 | else: 57 | html += "
  • %s: %s
  • " % (k, dict_to_li(v, html)) 58 | html += "" 59 | return html 60 | 61 | 62 | @app.template_filter("dict_to_ul") 63 | def dict_to_ul(s): 64 | """ 65 | Helper to convert recursively dict elements to an html unsorted list 66 | """ 67 | app.logger.debug("Flattening %s" % s) 68 | result = '