Page MenuHomeDevCentral

D3494.id9155.diff
No OneTemporary

D3494.id9155.diff

diff --git a/PORTS b/PORTS
--- a/PORTS
+++ b/PORTS
@@ -1,3 +1,6 @@
+devserver
+ 2337 API api-exec HTTP
+
webserver-alkane
9253 php-fpm metrics
diff --git a/roles/devserver/api-exec/config.sls b/roles/devserver/api-exec/config.sls
new file mode 100644
--- /dev/null
+++ b/roles/devserver/api-exec/config.sls
@@ -0,0 +1,28 @@
+# -------------------------------------------------------------
+# API :: api-exec
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# -------------------------------------------------------------
+
+{% set network = salt['node.resolve_network']() %}
+{% set half_num_cpus = grains["num_cpus"] / 2 %}
+
+/usr/local/etc/api-exec.conf:
+ file.managed:
+ - source: salt://roles/devserver/api-exec/files/api-exec.conf
+ - template: jinja
+ - context:
+ internal_ip: {{ network["private_ipv4_address"] }}
+ port: 2337
+
+ processes: {{ half_num_cpus | int }}
+
+ paths:
+ app: /srv/api-exec/src
+ venv: /srv/api-exec/venv
+
+/var/log/api-exec.log:
+ file.managed:
+ - user: nobody
+ - replace: False
diff --git a/roles/devserver/api-exec/files/api-exec.conf b/roles/devserver/api-exec/files/api-exec.conf
new file mode 100644
--- /dev/null
+++ b/roles/devserver/api-exec/files/api-exec.conf
@@ -0,0 +1,69 @@
+# -------------------------------------------------------------
+# API :: api-exec
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# Source file: roles/devserver/api-exec/files/api-exec.conf
+# -------------------------------------------------------------
+#
+# <auto-generated>
+# This file is managed by our rOPS SaltStack repository.
+#
+# Changes to this file may cause incorrect behavior
+# and will be lost if the state is redeployed.
+# </auto-generated>
+
+# -------------------------------------------------------------
+# uWSGI
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+uwsgi:
+ module: server
+ callable: app
+
+ http-socket: {{ internal_ip }}:{{ port }}
+
+ master: true
+
+ processes: {{ processes }}
+ threads: 2
+
+ chdir: {{ paths.app }}
+ virtualenv: {{ paths.venv }}
+
+ safe-pidfile: /var/run/api-exec.pid
+
+# -------------------------------------------------------------
+# Global environment
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+env:
+ CCACHE_DIR: /var/cache/ccache
+ PATH: "/bin:/usr/bin:/usr/local/bin"
+
+# -------------------------------------------------------------
+# Routes
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+routes:
+ /status: echo 200 ALIVE
+
+ /motd: motd
+ /uptime: uptime
+ /uname: uname -a
+
+ /fortune: fortune
+ /fortune/bofh: fortune bofh
+ /fortune/epictetus: fortune epictetus
+ /fortune/freebsd: fortune freebsd-classic
+ /fortune/futurama: fortune futurama
+ /fortune/tips: fortune freebsd-tips
+
+ /metrics/ccache:
+ command: ccache-metrics
+ mime_type: application/openmetrics-text
+
+ /version: cat /etc/os-release
+ /version/runtime: freebsd-version -r
+ /version/kernel: freebsd-version -k
+ /version/userland: freebsd-version -u
diff --git a/roles/devserver/api-exec/files/rc/api_exec b/roles/devserver/api-exec/files/rc/api_exec
new file mode 100644
--- /dev/null
+++ b/roles/devserver/api-exec/files/rc/api_exec
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+# PROVIDE: api_exec
+# REQUIRE: DAEMON
+# KEYWORD: shutdown
+
+# -------------------------------------------------------------
+# API :: api-exec
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# Source file: roles/devserver/api-exec/files/rc/api_exec
+# -------------------------------------------------------------
+#
+# <auto-generated>
+# This file is managed by our rOPS SaltStack repository.
+#
+# Changes to this file may cause incorrect behavior
+# and will be lost if the state is redeployed.
+# </auto-generated>
+
+# Add the following lines to /etc/rc.conf.local or /etc/rc.conf
+# to enable this service:
+# api_exec_enable (bool): Set it to YES to enable api_exec.
+# Default is "NO"
+# api_exec_user (user): Set user to run api_exec
+# Default is "nobody".
+# api_exec_config (path): The configuration file to use.
+# Default is "/usr/local/etc/api-exec.conf".
+# api_exec_venv (path): Path to Python virtual environment to use.
+# Default is "/srv/api-exec/venv".
+# api_exec_logfile (path): Path to the service log.
+# Default is "/var/log/api-exec.log".
+
+. /etc/rc.subr
+
+name="api_exec"
+rcvar="${name}_enable"
+
+load_rc_config $name
+
+: ${api_exec_enable:="NO"}
+: ${api_exec_user:="nobody"}
+: ${api_exec_config:="/usr/local/etc/api-exec.conf"}
+: ${api_exec_venv:="/srv/api-exec/venv"}
+: ${api_exec_logfile:="/var/log/api-exec.log"}
+
+pidfile="/var/run/api-exec.pid"
+command="${api_exec_venv}/bin/uwsgi"
+command_args="--master --yaml ${api_exec_config} --daemonize2=${api_exec_logfile}"
+
+sig_stop="INT"
+start_precmd=api_exec_startprecmd
+stop_postcmd=api_exec_stop_postcmd
+
+api_exec_startprecmd()
+{
+ touch ${pidfile} ${api_exec_logfile}
+ chown ${api_exec_user} ${pidfile} ${api_exec_logfile}
+}
+
+api_exec_stop_postcmd()
+{
+ rm -f ${pidfile}
+}
+
+run_rc_command "$1"
diff --git a/roles/devserver/api-exec/files/rc/api_exec.conf b/roles/devserver/api-exec/files/rc/api_exec.conf
new file mode 100644
--- /dev/null
+++ b/roles/devserver/api-exec/files/rc/api_exec.conf
@@ -0,0 +1,16 @@
+# -------------------------------------------------------------
+# API :: api-exec
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# Source file: roles/devserver/api-exec/files/rc/api_exec.conf
+# -------------------------------------------------------------
+#
+# <auto-generated>
+# This file is managed by our rOPS SaltStack repository.
+#
+# Changes to this file may cause incorrect behavior
+# and will be lost if the state is redeployed.
+# </auto-generated>
+
+api_exec_enable="YES"
diff --git a/roles/devserver/api-exec/files/server.py b/roles/devserver/api-exec/files/server.py
new file mode 100755
--- /dev/null
+++ b/roles/devserver/api-exec/files/server.py
@@ -0,0 +1,149 @@
+#!/usr/bin/env python3
+
+# -------------------------------------------------------------
+# API to execute commands
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# Description: Run commands
+# License: BSD-2-Clause
+# Source file: roles/devserver/api-exec/files/server.py
+# -------------------------------------------------------------
+#
+# <auto-generated>
+# This file is managed by our rOPS SaltStack repository.
+#
+# Changes to this file may cause incorrect behavior
+# and will be lost if the state is redeployed.
+# </auto-generated>
+
+
+import os
+import subprocess
+import sys
+
+from flask import Flask, Response, abort
+import yaml
+
+
+# -------------------------------------------------------------
+# Parse configuration
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+DEFAULT_CONFIGURATION_FILE = "/usr/local/etc/api-exec.conf"
+DEFAULT_MIME_TYPE = "text/plain"
+
+
+def load_config(config_file=None):
+ if config_file is None:
+ config_file = DEFAULT_CONFIGURATION_FILE
+
+ with open(config_file, "r") as file:
+ config = yaml.safe_load(file)
+
+ config["routes"] = {
+ key: ApiExecRoute.parse_config_entry(value)
+ for key, value in config["routes"].items()
+ }
+
+ return config
+
+
+def set_environment(env):
+ for key, value in env.items():
+ os.environ[key] = value
+
+
+class ApiExecRoute:
+ def __init__(self, command, mime_type):
+ self.command = command
+ self.mime_type = mime_type
+
+ @staticmethod
+ def parse_config_entry(entry):
+ if type(entry) is str:
+ return ApiExecRoute(entry, DEFAULT_MIME_TYPE)
+
+ mime_type = entry.get("mime_type", DEFAULT_MIME_TYPE)
+ return ApiExecRoute(entry["command"], mime_type)
+
+
+# -------------------------------------------------------------
+# Run commands
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+def execute_command(command):
+ result = subprocess.run(
+ command.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
+ )
+
+ return result.stdout, result.stderr, result.returncode == 0
+
+
+# -------------------------------------------------------------
+# Build Flask application
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+def initialize_app():
+ app = Flask(__name__)
+
+ app_config = load_config(os.environ.get("APP_EXEC_CONFIG_PATH"))
+ if not app_config:
+ print("Can't load configuration", file=sys.stderr)
+ sys.exit(1)
+
+ set_environment(app_config.get("env", {}))
+ app.config["routes"] = app_config["routes"]
+
+ return app
+
+
+app = initialize_app()
+
+
+# -------------------------------------------------------------
+# Requests
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+@app.route("/<route>", methods=["GET"])
+def get_level1(route):
+ return handle_command_request("/" + route)
+
+
+@app.route("/<route>/<subroute>", methods=["GET"])
+def get_level2(route, subroute):
+ return handle_command_request(f"/{route}/{subroute}")
+
+
+def handle_command_request(route):
+ args = app.config.get("routes", {}).get(route, None)
+
+ if not args:
+ return abort(404, description=f"Route '{route}' not found")
+
+ output, error, is_ok = execute_command(args.command)
+
+ if not is_ok:
+ if error is None or error.strip() == "":
+ description = "Error running command"
+ else:
+ description = f"Error running command: {error}"
+ return abort(500, description=description)
+
+ resp = Response(output, mimetype=args.mime_type)
+ resp.headers["Access-Control-Allow-Origin"] = "*"
+ resp.headers["X-API-Exec-Command"] = args.command
+
+ return resp
+
+
+# -------------------------------------------------------------
+# Application entry point
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+
+if __name__ == "__main__":
+ app.run(host="127.0.0.1", port=8000)
diff --git a/roles/devserver/init.sls b/roles/devserver/api-exec/init.sls
copy from roles/devserver/init.sls
copy to roles/devserver/api-exec/init.sls
--- a/roles/devserver/init.sls
+++ b/roles/devserver/api-exec/init.sls
@@ -1,18 +1,11 @@
# -------------------------------------------------------------
-# Salt — Provision a development server
+# API :: api-exec
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Project: Nasqueron
-# Created: 2017-10-20
# License: Trivial work, not eligible to copyright
# -------------------------------------------------------------
include:
- - .datacube
- - .dns
- - .mail
- - .pkg
- - .userland-software
- - .userland-home
- - .poudriere
- - .webserver-home
- - .webserver-wwwroot51
+ - .software
+ - .config
+ - .service
diff --git a/roles/devserver/api-exec/service.sls b/roles/devserver/api-exec/service.sls
new file mode 100644
--- /dev/null
+++ b/roles/devserver/api-exec/service.sls
@@ -0,0 +1,21 @@
+# -------------------------------------------------------------
+# API :: api-exec
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# -------------------------------------------------------------
+
+{% from "map.jinja" import dirs, services with context %}
+
+{% if services["manager"] == "rc" %}
+
+{{ dirs.etc }}/rc.d/api_exec:
+ file.managed:
+ - source: salt://roles/devserver/api-exec/files/rc/api_exec
+ - mode: 755
+
+/etc/rc.conf.d/api_exec:
+ file.managed:
+ - source: salt://roles/devserver/api-exec/files/rc/api_exec.conf
+
+{% endif %}
diff --git a/roles/devserver/api-exec/software.sls b/roles/devserver/api-exec/software.sls
new file mode 100644
--- /dev/null
+++ b/roles/devserver/api-exec/software.sls
@@ -0,0 +1,33 @@
+# -------------------------------------------------------------
+# API :: api-exec
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# License: Trivial work, not eligible to copyright
+# -------------------------------------------------------------
+
+# -------------------------------------------------------------
+# Source code
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+/srv/api-exec/src/server.py:
+ file.managed:
+ - source: salt://roles/devserver/api-exec/files/server.py
+ - makedirs: True
+ - mode: 755
+
+# -------------------------------------------------------------
+# Python virtual environment
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+/srv/api-exec/venv:
+ file.directory:
+ - user: deploy
+
+api_exec_virtualenv:
+ cmd.run:
+ - name: |
+ python3 -m venv /srv/api-exec/venv && \
+ . /srv/api-exec/venv/bin/activate && \
+ pip install Flask PyYAML uwsgi
+ - creates: /srv/api-exec/venv/bin/activate
+ - runas: deploy
diff --git a/roles/devserver/init.sls b/roles/devserver/init.sls
--- a/roles/devserver/init.sls
+++ b/roles/devserver/init.sls
@@ -14,5 +14,9 @@
- .userland-software
- .userland-home
- .poudriere
+
+ # Needs userland-software
+ - .api-exec
+
- .webserver-home
- .webserver-wwwroot51

File Metadata

Mime Type
text/plain
Expires
Mon, Nov 18, 18:23 (19 h, 55 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2250117
Default Alt Text
D3494.id9155.diff (13 KB)

Event Timeline