UI - copy from helpers, systemd service and instances page update
This commit is contained in:
@@ -3,7 +3,7 @@ import json, uuid, glob, copy, re, subprocess
|
||||
class Config :
|
||||
|
||||
def __init__(self) :
|
||||
with open("/opt/settings.json", "r") as f :
|
||||
with open("/opt/bunkerized-nginx/settings.json", "r") as f :
|
||||
self.__settings = json.loads(f.read())
|
||||
|
||||
def __env_to_dict(self, filename) :
|
||||
@@ -37,7 +37,7 @@ class Config :
|
||||
conf["SERVER_NAME"] = " ".join(servers)
|
||||
env_file = "/tmp/" + str(uuid.uuid4()) + ".env"
|
||||
self.__dict_to_env(env_file, conf)
|
||||
proc = subprocess.run(["/bin/su", "-c", "/opt/gen/main.py --settings /opt/settings.json --templates /opt/confs --output /etc/nginx --variables " + env_file, "nginx"], capture_output=True)
|
||||
proc = subprocess.run(["/opt/bunkerized-nginx/gen/main.py", "--settings", "/opt/bunkerized-nginx/settings.json", "--templates", "/opt/bunkerized-nginx/confs", "--output", "/etc/nginx", "--variables", env_file], capture_output=True)
|
||||
stderr = proc.stderr.decode("ascii")
|
||||
#stdout = proc.stdout.decode("ascii")
|
||||
if stderr != "" or proc.returncode != 0 :
|
||||
|
||||
@@ -5,11 +5,11 @@ RUN chmod +x /tmp/dependencies.sh && \
|
||||
/tmp/dependencies.sh && \
|
||||
rm -f /tmp/dependencies.sh
|
||||
|
||||
COPY gen/ /opt/gen
|
||||
COPY confs/site/ /opt/confs/site
|
||||
COPY confs/global/ /opt/confs/global
|
||||
COPY ui/ /opt/entrypoint
|
||||
COPY settings.json /opt
|
||||
COPY gen/ /opt/bunkerized-nginx/gen
|
||||
COPY confs/site/ /opt/bunkerized-nginx/confs/site
|
||||
COPY confs/global/ /opt/bunkerized-nginx/confs/global
|
||||
COPY ui/ /opt/bunkerized-nginx/ui
|
||||
COPY settings.json /opt/bunkerized-nginx
|
||||
|
||||
COPY ui/prepare.sh /tmp
|
||||
RUN chmod +x /tmp/prepare.sh && \
|
||||
@@ -18,6 +18,6 @@ RUN chmod +x /tmp/prepare.sh && \
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
WORKDIR /opt/entrypoint
|
||||
ENV FLASK_APP entrypoint.py
|
||||
ENTRYPOINT ["/usr/bin/python3", "-m", "flask", "run", "--host=0.0.0.0"]
|
||||
WORKDIR /opt/bunkerized-nginx/ui
|
||||
USER nginx:nginx
|
||||
ENTRYPOINT ["/usr/bin/gunicorn", "--bind", "unix:bunkerized-nginx-ui.sock", "-m", "007", "wsgi:app"]
|
||||
12
ui/bunkerized-nginx-ui.service
Normal file
12
ui/bunkerized-nginx-ui.service
Normal file
@@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description=Web UI for bunkerized-nginx
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=nginx
|
||||
Group=nginx
|
||||
WorkingDirectory=/opt/bunkerized-nginx/ui
|
||||
ExecStart=gunicorn --bind unix:bunkerized-nginx-ui.sock -m 007 wsgi:app
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
apk add py3-pip bash
|
||||
pip3 install docker flask flask-login
|
||||
pip3 install docker flask flask-login requests gunicorn
|
||||
|
||||
@@ -2,20 +2,18 @@
|
||||
|
||||
from flask import Flask, render_template, current_app, request
|
||||
|
||||
from src.Instances import Instances
|
||||
|
||||
from Docker import Docker
|
||||
from Config import Config
|
||||
import utils
|
||||
import os, json, re, copy, traceback
|
||||
|
||||
app = Flask(__name__, static_url_path="/", static_folder="static", template_folder="templates")
|
||||
ABSOLUTE_URI = ""
|
||||
if "ABSOLUTE_URI" in os.environ :
|
||||
ABSOLUTE_URI = os.environ["ABSOLUTE_URI"]
|
||||
DOCKER_HOST = "unix:///var/run/docker.sock"
|
||||
if "DOCKER_HOST" in os.environ :
|
||||
DOCKER_HOST = os.environ["DOCKER_HOST"]
|
||||
app.config["ABSOLUTE_URI"] = ABSOLUTE_URI
|
||||
app.config["DOCKER"] = Docker(DOCKER_HOST)
|
||||
|
||||
vars = utils.get_variables()
|
||||
app.config["ABSOLUTE_URI"] = vars["ABSOLUTE_URI"]
|
||||
app.config["INSTANCES"] = Instances(vars["DOCKER_HOST"], vars["API_URI"])
|
||||
app.config["CONFIG"] = Config()
|
||||
app.jinja_env.globals.update(env_to_summary_class=utils.env_to_summary_class)
|
||||
app.jinja_env.globals.update(form_service_gen=utils.form_service_gen)
|
||||
@@ -26,7 +24,7 @@ app.jinja_env.globals.update(form_service_gen_multiple_values=utils.form_service
|
||||
@app.route('/home')
|
||||
def home() :
|
||||
try :
|
||||
instances_number = len(app.config["DOCKER"].get_instances())
|
||||
instances_number = len(app.config["INSTANCES"].get_instances())
|
||||
services_number = len(app.config["CONFIG"].get_services())
|
||||
return render_template("home.html", title="Home", instances_number=instances_number, services_number=services_number)
|
||||
except Exception as e :
|
||||
@@ -40,7 +38,7 @@ def instances() :
|
||||
if request.method == "POST" :
|
||||
|
||||
# Check operation
|
||||
if not "operation" in request.form or not request.form["operation"] in ["reload", "start", "stop", "restart", "delete"] :
|
||||
if not "operation" in request.form or not request.form["operation"] in ["reload", "start", "stop", "restart"] :
|
||||
raise Exception("Missing operation parameter on /instances.")
|
||||
|
||||
# Check that all fields are present
|
||||
@@ -49,18 +47,16 @@ def instances() :
|
||||
|
||||
# Do the operation
|
||||
if request.form["operation"] == "reload" :
|
||||
operation = app.config["DOCKER"].reload_instance(request.form["INSTANCE_ID"])
|
||||
operation = app.config["INSTANCES"].reload_instance(request.form["INSTANCE_ID"])
|
||||
elif request.form["operation"] == "start" :
|
||||
operation = app.config["DOCKER"].start_instance(request.form["INSTANCE_ID"])
|
||||
operation = app.config["INSTANCES"].start_instance(request.form["INSTANCE_ID"])
|
||||
elif request.form["operation"] == "stop" :
|
||||
operation = app.config["DOCKER"].stop_instance(request.form["INSTANCE_ID"])
|
||||
operation = app.config["INSTANCES"].stop_instance(request.form["INSTANCE_ID"])
|
||||
elif request.form["operation"] == "restart" :
|
||||
operation = app.config["DOCKER"].restart_instance(request.form["INSTANCE_ID"])
|
||||
elif request.form["operation"] == "delete" :
|
||||
operation = app.config["DOCKER"].delete_instance(request.form["INSTANCE_ID"])
|
||||
operation = app.config["INSTANCES"].restart_instance(request.form["INSTANCE_ID"])
|
||||
|
||||
# Display instances
|
||||
instances = app.config["DOCKER"].get_instances()
|
||||
instances = app.config["INSTANCES"].get_instances()
|
||||
return render_template("instances.html", title="Instances", instances=instances, operation=operation)
|
||||
|
||||
except Exception as e :
|
||||
|
||||
@@ -8,7 +8,10 @@ adduser -h /var/cache/nginx -g nginx -s /bin/sh -G nginx -D -H -u 101 nginx
|
||||
chown -R root:nginx /opt
|
||||
find /opt -type f -exec chmod 0740 {} \;
|
||||
find /opt -type d -exec chmod 0750 {} \;
|
||||
chmod ugo+x /opt/entrypoint/*
|
||||
chmod ugo+x /opt/gen/main.py
|
||||
chmod 770 /opt
|
||||
chmod 440 /opt/settings.json
|
||||
chmod 750 /opt/bunkerized-nginx/gen/main.py
|
||||
|
||||
# prepare /var/log
|
||||
mkdir /var/log/nginx
|
||||
chown root:nginx /var/log/nginx
|
||||
chmod 750 /var/log/nginx
|
||||
ln -s /proc/1/fd/1 /var/log/nginx/ui.log
|
||||
@@ -2,19 +2,20 @@ import docker, os, requests
|
||||
|
||||
class Instances :
|
||||
|
||||
def __init__(self, docker_host, api) :
|
||||
def __init__(self, docker_host, api_uri) :
|
||||
try :
|
||||
self.__docker = docker.DockerClient(base_url=docker_host)
|
||||
except :
|
||||
self.__docker = None
|
||||
self.__api = api
|
||||
self.__api_uri = api_uri
|
||||
|
||||
def __instance(self, name, type, status, data=None) :
|
||||
def __instance(self, id, name, type, status, data=None) :
|
||||
instance = {}
|
||||
instance["name"] = name
|
||||
instance["type"] = type
|
||||
instance["status"] = status
|
||||
instance["data"] = data
|
||||
instance["id"] = id
|
||||
instance["name"] = name
|
||||
instance["type"] = type
|
||||
instance["status"] = status
|
||||
instance["data"] = data
|
||||
|
||||
def __api_request(self, instance, order) :
|
||||
result = True
|
||||
@@ -27,7 +28,7 @@ class Instances :
|
||||
hosts.append(host)
|
||||
for host in hosts :
|
||||
try :
|
||||
req = requests.post("http://" + host + ":8080" + self.__api + order)
|
||||
req = requests.post("http://" + host + ":8080" + self.__api_uri + order)
|
||||
if not req or req.status_code != 200 or req.text != "ok" :
|
||||
result = False
|
||||
except :
|
||||
@@ -40,14 +41,16 @@ class Instances :
|
||||
if self.__docker != None :
|
||||
if self.__docker.swarm == None :
|
||||
for instance in self.__docker.containers.list(all=True, filters={"label" : "bunkerized-nginx.UI"}) :
|
||||
id = instance.id
|
||||
name = instance.name
|
||||
type = "container"
|
||||
status = "down"
|
||||
if instance.status == "running" :
|
||||
status = "up"
|
||||
instances.append(self.__instance(name, type, status, instance))
|
||||
instances.append(self.__instance(id, name, type, status, instance))
|
||||
else :
|
||||
for instance in self.__docker.services.list(all=True, filters={"label" : "bunkerized-nginx.UI"}) :
|
||||
id = instance.id
|
||||
name = instance.name
|
||||
type = "service"
|
||||
status = "down"
|
||||
@@ -55,16 +58,17 @@ class Instances :
|
||||
running_tasks = instance.attrs["ServiceStatus"]["RunningTasks"]
|
||||
if desired_tasks > 0 and (desired_tasks == running_tasks) :
|
||||
status = "up"
|
||||
instances.append(self.__instance(name, type, status, instance))
|
||||
instances.append(self.__instance(id, name, type, status, instance))
|
||||
|
||||
# Local instance
|
||||
if os.path.exists("/usr/sbin/nginx") :
|
||||
id = "local"
|
||||
name = "local"
|
||||
type = "local"
|
||||
status = "down"
|
||||
if os.path.exists("/tmp/nginx.pid") :
|
||||
status = "up"
|
||||
instances.append(self.__instance(name, type, status))
|
||||
instances.append(self.__instance(id, name, type, status))
|
||||
|
||||
return instances
|
||||
|
||||
@@ -99,7 +103,7 @@ class Instances :
|
||||
proc = subprocess.run(["/usr/sbin/nginx", "-g", "daemon on;"], capture_output=True)
|
||||
result = proc.returncode == 0
|
||||
elif instance["type"] == "container" or instance["type"] == "service" :
|
||||
result = self.__api_request(instance, "/start")
|
||||
result = False #self.__api_request(instance, "/start")
|
||||
if result :
|
||||
return "Instance " + instance["name"] + " has been started."
|
||||
return "Can't start " + instance["name"]
|
||||
@@ -123,7 +127,7 @@ class Instances :
|
||||
proc = subprocess.run(["/usr/sbin/nginx", "-g", "daemon on;"], capture_output=True)
|
||||
result = proc.returncode == 0
|
||||
elif instance["type"] == "container" or instance["type"] == "service" :
|
||||
result = self.__api_request(instance, "/restart")
|
||||
result = False #self.__api_request(instance, "/restart")
|
||||
if result :
|
||||
return "Instance " + instance["name"] + " has been restarted."
|
||||
return "Can't restart " + instance["name"]
|
||||
@@ -17,11 +17,9 @@
|
||||
|
||||
{% for instance in instances %}
|
||||
{% set color = "dark" %}
|
||||
{% if instance["status"] == "running" %}
|
||||
{% if instance["status"] == "up" %}
|
||||
{% set color = "success" %}
|
||||
{% elif instance["status"] == "created" or instance["status"] == "restarting" or instance["status"] == "paused" %}
|
||||
{% set color = "warning" %}
|
||||
{% elif instance["status"] == "exited" or instance["status"] == "dead" %}
|
||||
{% elif instance["status"] == "down" %}
|
||||
{% set color = "danger" %}
|
||||
{% endif %}
|
||||
|
||||
@@ -34,27 +32,20 @@
|
||||
{{ instance["name"] }}
|
||||
<div class="btn-group mx-2 float-end" role="group">
|
||||
<button id="btnGroupDrop1" class="btn btn-sm dropdown-toggle btn-light" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="fas fa-cogs"> manage container</i>
|
||||
<i class="fas fa-cogs"> manage instance</i>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="btnGroupDrop1">
|
||||
<li><a class="dropdown-item" href="#" onClick="return startInstance('{{ instance["id"] }}');">Start</a></li>
|
||||
{% if instance["type"] == "local" %}<li><a class="dropdown-item" href="#" onClick="return startInstance('{{ instance["id"] }}');">Start</a></li>{% endif %}
|
||||
<li><a class="dropdown-item" href="#" onClick="return stopInstance('{{ instance["id"] }}');">Stop</a></li>
|
||||
<li><a class="dropdown-item" href="#" onClick="return restartInstance('{{ instance["id"] }}');">Restart</a></li>
|
||||
<li><a class="dropdown-item" href="#" onClick="return deleteInstance('{{ instance["id"] }}');">Delete</a></li>
|
||||
{% if instance["type"] == "local" %}<li><a class="dropdown-item" href="#" onClick="return restartInstance('{{ instance["id"] }}');">Restart</a></li>{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
<button class="btn btn-sm mx-2 float-end btn-light" onClick="reloadInstance('{{ instance["id"] }}');"><i class="fas fa-redo-alt"></i> reload nginx</button>
|
||||
<button class="btn btn-sm mx-2 float-end btn-light" onClick="reloadInstance('{{ instance["id"] }}');"><i class="fas fa-redo-alt"></i> Reload configuration</button>
|
||||
</div>
|
||||
<div class="card-body text-dark">
|
||||
<h5 class="card-title">Status : {{ instance["status"] }}</h5>
|
||||
<span class="card-text">
|
||||
Environment variables :<br />
|
||||
{% set envfilter = ["PATH", "NGINX_VERSION", "NJS_VERSION", "PKG_RELEASE"] %}
|
||||
{% for env in instance.attrs["Config"]["Env"] %}
|
||||
{% if not env.startswith("PATH=") and not env.startswith("NGINX_VERSION=") and not env.startswith("NJS_VERSION=") and not env.startswith("PKG_RELEASE=") %}
|
||||
{{ env }}<br />
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
TODO
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -63,4 +54,4 @@
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
15
ui/utils.py
15
ui/utils.py
@@ -1,9 +1,20 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import datetime, re, json
|
||||
import datetime, re, json, os
|
||||
|
||||
def get_variables() :
|
||||
vars = {}
|
||||
vars["DOCKER_HOST"] = "unix:///var/run/docker.sock"
|
||||
vars["API_URI"] = ""
|
||||
vars["ABSOLUTE_URI"] = ""
|
||||
for k in vars :
|
||||
if k in os.environ :
|
||||
vars[k] = os.environ[k]
|
||||
return vars
|
||||
|
||||
def log(event) :
|
||||
print("[" + str(datetime.datetime.now().replace(microsecond=0)) + "] " + event, flush=True)
|
||||
with open("/var/log/nginx/ui.log", "a") as f :
|
||||
f.write("[" + str(datetime.datetime.now().replace(microsecond=0)) + "] " + event + "\n")
|
||||
|
||||
def env_to_summary_class(var, value) :
|
||||
if type(var) is list and type(value) is list :
|
||||
|
||||
4
ui/wsgi.py
Normal file
4
ui/wsgi.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from entrypoint import app
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
Reference in New Issue
Block a user