chore: Finalize install scripts.
This commit is contained in:
parent
61ccbf61d6
commit
9006a8e002
7 changed files with 191 additions and 39 deletions
|
@ -1,24 +1,37 @@
|
|||
#!/usr/bin/env bash
|
||||
SERVER_INSTALL_PATH="$1"
|
||||
EXTERN_IP="$2"
|
||||
|
||||
HTTP_PORT="$((1024 + $RANDOM % 65535))"
|
||||
TMPDIR="/tmp/server_http_$HTTP_PORT"
|
||||
|
||||
BASE_IPS="$(ip a | grep "inet" | grep "brd" | cut -d "/" -f 1 | cut -d " " -f 6)"
|
||||
|
||||
EXT_10_DOT_IPS="$(echo "$BASE_IPS" | grep "10.")"
|
||||
EXT_192168_IPS="$(echo "$BASE_IPS" | grep "192.168.")"
|
||||
EXT_172_16_IPS="$(echo "$BASE_IPS" | grep "172.16.")"
|
||||
|
||||
EXTERNAL_IP_FULL=$EXT_10_DOT_IPS$'\n'$EXT_192168_IPS$'\n'$EXT_172_16_IPS$'\n'
|
||||
|
||||
if [ "$SERVER_INSTALL_PATH" = "" ]; then
|
||||
if [ "$SERVER_INSTALL_PATH" == "" ]; then
|
||||
echo "You didn't pass in all the arguments! Usage:"
|
||||
echo " ./install.sh \$INSTALL_KEY"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
./merge.py "$SERVER_INSTALL_PATH"
|
||||
if [ "$EXTERN_IP" == "" ]; then
|
||||
BASE_IPS="$(ip a | grep "inet" | grep "brd" | cut -d "/" -f 1 | cut -d " " -f 6)"
|
||||
|
||||
EXT_10_DOT_IP="$(echo "$BASE_IPS" | grep "10." | cut -d $'\n' -f 1)"
|
||||
EXT_172_16_IP="$(echo "$BASE_IPS" | grep "172.16." | cut -d $'\n' -f 1)"
|
||||
EXT_192168_IP="$(echo "$BASE_IPS" | grep "192.168." | cut -d $'\n' -f 1)"
|
||||
|
||||
if [ "$EXT_10_DOT_IP" != "" ]; then
|
||||
EXTERN_IP="$EXT_10_DOT_IP"
|
||||
fi
|
||||
|
||||
if [ "$EXT_172_16_IP" != "" ]; then
|
||||
EXTERN_IP="$EXT_172_16_IP"
|
||||
fi
|
||||
|
||||
if [ "$EXT_192168_IP" != "" ]; then
|
||||
EXTERN_IP="$EXT_192168_IP"
|
||||
fi
|
||||
fi
|
||||
|
||||
./merge.py "$SERVER_INSTALL_PATH" "http://$EXTERN_IP:$HTTP_PORT/api/installer_update_webhook"
|
||||
|
||||
echo "[x] initializing..."
|
||||
mkdir $TMPDIR
|
||||
|
@ -35,18 +48,12 @@ touch $TMPDIR/meta-data
|
|||
touch $TMPDIR/vendor-data
|
||||
|
||||
echo "[x] starting HTTP server..."
|
||||
echo " - Listening on port $HTTP_PORT."
|
||||
echo " - Add one of these command line options for Ubuntu (guessed local IP):"
|
||||
echo " - Going to listen on port $HTTP_PORT."
|
||||
echo " - Unless you believe the install has gone wrong, do NOT manually kill the HTTP server,"
|
||||
echo " - as it will close on its own."
|
||||
echo " - Add these command line options to Ubuntu:"
|
||||
echo " - autoinstall \"ds=nocloud-net;s=http://$EXTERN_IP:$HTTP_PORT/\""
|
||||
|
||||
while IFS= read -r IP; do
|
||||
# I'm too lazy to do root causing of this shit.
|
||||
|
||||
if [ "$IP" != "" ]; then
|
||||
echo " - autoinstall \"ds=nocloud-net;s=http://$IP:$HTTP_PORT/\""
|
||||
fi
|
||||
done <<< "$EXTERNAL_IP_FULL"
|
||||
|
||||
echo " - Choose the right IP."
|
||||
echo
|
||||
|
||||
SERVE_SCRIPT="$PWD/serve.py"
|
||||
|
|
19
serverinfra/k3s.yaml
Normal file
19
serverinfra/k3s.yaml
Normal file
|
@ -0,0 +1,19 @@
|
|||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkakNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUzTWpJMk1USXpOelV3SGhjTk1qUXdPREF5TVRVeU5qRTFXaGNOTXpRd056TXhNVFV5TmpFMQpXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUzTWpJMk1USXpOelV3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFTcGhGejc1VUVxdHFlb3RKSUhWN0U4TkRuMVJXTHlrUXQ5UHdKTkdNT2kKS2h3WHRkbkM3aHRpRHFXcDJneEI5OStJSHlvdlc4VlJDcFkxdnpPcEtKdERvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVXdlNzhWbk5hRkpVeENVQlZKa0xOCkhRLzd3Tm93Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnWndmbjBYSWFoQ3VIa1JuVTMwMmVkd3ZxU1BiL2ZKR24KM0t4QXdVb0p4cG9DSUExZlRjMU1VbkFHdHVxd0RDR3FGdndndmVqOXNBUnJZekVtcitZelRzelMKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
|
||||
server: https://127.0.0.1:6443
|
||||
name: default
|
||||
contexts:
|
||||
- context:
|
||||
cluster: default
|
||||
user: default
|
||||
name: default
|
||||
current-context: default
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: default
|
||||
user:
|
||||
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJrVENDQVRlZ0F3SUJBZ0lJQlpxNlZMVm91R0l3Q2dZSUtvWkl6ajBFQXdJd0l6RWhNQjhHQTFVRUF3d1kKYXpOekxXTnNhV1Z1ZEMxallVQXhOekl5TmpFeU16YzFNQjRYRFRJME1EZ3dNakUxTWpZeE5Wb1hEVEkxTURndwpNakUxTWpZeE5Wb3dNREVYTUJVR0ExVUVDaE1PYzNsemRHVnRPbTFoYzNSbGNuTXhGVEFUQmdOVkJBTVRESE41CmMzUmxiVHBoWkcxcGJqQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJFS25jd2Jmc3NYdGxXVFMKb05LYTcyOHpFSDRZTkVoVUIzU0hRNXhzb2lMYzdEVmVpRjNpd0hER1FxNlpuWnFyRXI4U1c3Q1ZzZ2N1di83TwpLSUcrRWVhalNEQkdNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUtCZ2dyQmdFRkJRY0RBakFmCkJnTlZIU01FR0RBV2dCUTMrRmM5TVFXTnA5TXN2czNIUDdoa1phVXJLVEFLQmdncWhrak9QUVFEQWdOSUFEQkYKQWlBWGtiYVVLQnBNdkM2S1ZTVDJsYlFjcTJ2WmRSMU44SWIyL05SMWZHdUFRZ0loQUtsNkczci9pbDU1dW00UQphcjEvNThWd0Nac3lVUDBBVlZZZFlBSjhxbmRxCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkekNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdFkyeHAKWlc1MExXTmhRREUzTWpJMk1USXpOelV3SGhjTk1qUXdPREF5TVRVeU5qRTFXaGNOTXpRd056TXhNVFV5TmpFMQpXakFqTVNFd0h3WURWUVFEREJock0zTXRZMnhwWlc1MExXTmhRREUzTWpJMk1USXpOelV3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFUOE45RFRodkZmazl2QU83WlMxWW1MVVUxYWhGVzJHcTUwZFZIOXlFNHcKc3hrbGZxemdmWXprc2dES2dmZVlLOFZIak5jaHA2VTh6cERFNm9wczRNaTdvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVU4vaFhQVEVGamFmVExMN054eis0ClpHV2xLeWt3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnUWQ1bVI3QzFZazdpaUpIYS9vUlQ4Q0R4MGpQZ2NITEkKaVBCZU9GL1RKTDBDSVFDdW5ic1B5dS9KWUNJQlhVMy9mdXhRcjg5MDJoeXBUT0NocDJvVENqU291Zz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
|
||||
client-key-data: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxIcnA5Rmh2a2ZJM2VJQTJiOVIxbTk4THh0RTBhaXRRcSt4REVVODhWRWJvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFUXFkekJ0K3l4ZTJWWk5LZzBwcnZiek1RZmhnMFNGUUhkSWREbkd5aUl0enNOVjZJWGVMQQpjTVpDcnBtZG1xc1N2eEpic0pXeUJ5Ni8vczRvZ2I0UjVnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
|
|
@ -11,11 +11,12 @@ for item in ["K3S_TOKEN", "SETUP_USERNAME", "SETUP_PASSWORD"]:
|
|||
print(f"ERROR: .env failed to load! (missing environment variable '{item}')")
|
||||
exit(1)
|
||||
|
||||
if len(argv) < 2:
|
||||
print("ERROR: Missing the server name")
|
||||
if len(argv) < 3:
|
||||
print("ERROR: Missing the server name or the webhook URL")
|
||||
exit(1)
|
||||
|
||||
server_name = argv[1]
|
||||
server_webhook_url = argv[2]
|
||||
|
||||
server_infra_contents = ""
|
||||
|
||||
|
@ -93,6 +94,8 @@ yaml_install_script["autoinstall"]["identity"]["hostname"] = infra_server["hostn
|
|||
yaml_install_script["autoinstall"]["identity"]["username"] = environ["SETUP_USERNAME"]
|
||||
yaml_install_script["autoinstall"]["identity"]["password"] = environ["SETUP_PASSWORD"]
|
||||
|
||||
yaml_install_script["autoinstall"]["reporting"]["hook"]["endpoint"] = server_webhook_url
|
||||
|
||||
ubuntu_install_contents = yaml.dump(yaml_install_script, Dumper=yaml.CDumper)
|
||||
|
||||
with open("/tmp/script.yml", "w") as new_install_script:
|
||||
|
|
|
@ -1,28 +1,146 @@
|
|||
# TODO:
|
||||
# Install logging over HTTP *could* be implemented (see autoinstall documentation), however
|
||||
# it is not implemented here.
|
||||
from termcolor import colored
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from os import getcwd, environ
|
||||
from pathlib import Path
|
||||
import socketserver
|
||||
import http.server
|
||||
import socket
|
||||
import json
|
||||
import sys
|
||||
|
||||
requests = set()
|
||||
def json_to_bytes(str: str) -> bytearray:
|
||||
return bytearray(json.dumps(str), "utf-8")
|
||||
|
||||
# Who needs Flask, anyways?
|
||||
class HTTPHandler(http.server.BaseHTTPRequestHandler):
|
||||
def send_headers(self):
|
||||
self.send_header("Content-Type", "application/json")
|
||||
self.end_headers()
|
||||
|
||||
def do_POST(self):
|
||||
if self.path == "/api/installer_update_webhook":
|
||||
content_length = 0
|
||||
|
||||
try:
|
||||
content_length = int(self.headers.get('Content-Length'))
|
||||
except ValueError:
|
||||
self.send_response(400)
|
||||
self.send_headers()
|
||||
|
||||
self.wfile.write(json_to_bytes({
|
||||
"success": False,
|
||||
"error": "Failed to decode Content-Length to read body",
|
||||
}))
|
||||
|
||||
return
|
||||
|
||||
resp_data = self.rfile.read(content_length).decode("utf-8")
|
||||
resp_decoded_data: dict = {}
|
||||
|
||||
try:
|
||||
resp_decoded_data = json.loads(resp_data)
|
||||
|
||||
if type(resp_decoded_data) is not dict:
|
||||
self.send_response(400)
|
||||
self.send_headers()
|
||||
|
||||
self.wfile.write(json_to_bytes({
|
||||
"success": False,
|
||||
"error": "Recieved invalid type for JSON",
|
||||
}))
|
||||
|
||||
return
|
||||
except json.JSONDecodeError:
|
||||
self.send_response(400)
|
||||
self.send_headers()
|
||||
|
||||
self.wfile.write(json_to_bytes({
|
||||
"success": False,
|
||||
"error": "Failed to decode JSON",
|
||||
}))
|
||||
|
||||
return
|
||||
|
||||
date_time = datetime.fromtimestamp(resp_decoded_data["timestamp"], timezone.utc)
|
||||
str_formatted_time = date_time.strftime("%H:%M:%S")
|
||||
|
||||
result_is_safe = resp_decoded_data["result"] == "SUCCESS" if "result" in resp_decoded_data else True
|
||||
output_file = sys.stdout if result_is_safe else sys.stderr
|
||||
|
||||
output_coloring = "light_blue"
|
||||
|
||||
if "result" in resp_decoded_data:
|
||||
res = resp_decoded_data["result"]
|
||||
|
||||
if res == "SUCCESS":
|
||||
output_coloring = "light_green"
|
||||
elif res == "WARN":
|
||||
output_coloring = "light_yellow"
|
||||
elif res == "FAIL":
|
||||
output_coloring = "light_red"
|
||||
|
||||
result_text_component = f" {resp_decoded_data["result"]} " if "result" in resp_decoded_data else " "
|
||||
final_output_text = f"{str_formatted_time} {resp_decoded_data["event_type"].upper()} {resp_decoded_data["level"]}:{result_text_component}{resp_decoded_data["name"]} ({resp_decoded_data["description"]})"
|
||||
|
||||
print(colored(final_output_text, output_coloring), file=output_file)
|
||||
|
||||
self.send_response(200)
|
||||
self.send_headers()
|
||||
|
||||
self.wfile.write(json_to_bytes({
|
||||
"success": True,
|
||||
}))
|
||||
|
||||
if resp_decoded_data["event_type"] == "finish" and resp_decoded_data["name"] == "subiquity/Shutdown/shutdown":
|
||||
print("\nSuccessfully finished installing!")
|
||||
exit(0)
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.send_headers()
|
||||
|
||||
self.wfile.write(json_to_bytes({
|
||||
"success": False,
|
||||
"error": "Unknown route"
|
||||
}))
|
||||
|
||||
class HTTPHandler(http.server.SimpleHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
http.server.SimpleHTTPRequestHandler.do_GET(self)
|
||||
requests.add(self.path)
|
||||
resolved_path = str(Path(self.path).resolve())
|
||||
file_path = getcwd() + resolved_path
|
||||
|
||||
found_meta_data = "/meta-data" in requests
|
||||
found_user_data = "/user-data" in requests
|
||||
found_vendor_data = "/vendor-data" in requests
|
||||
try:
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
|
||||
if found_meta_data and found_user_data and found_vendor_data:
|
||||
print("[x] sent all our data, exiting...")
|
||||
sys.exit(0)
|
||||
with open(file_path, "rb") as file:
|
||||
self.wfile.write(file.read())
|
||||
except (FileNotFoundError, IsADirectoryError):
|
||||
self.send_response(404)
|
||||
self.send_headers()
|
||||
|
||||
server = socketserver.TCPServer(("", int(sys.argv[1])), HTTPHandler)
|
||||
self.wfile.write(json_to_bytes({
|
||||
"success": False,
|
||||
"error": "file not found"
|
||||
}))
|
||||
except () as exception:
|
||||
exception.print_exception()
|
||||
|
||||
def log_message(self, format: str, *args):
|
||||
status_code = 0
|
||||
|
||||
try:
|
||||
status_code = int(args[1])
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Disable logging for the /api/ endpoint for POST requests unless the error code > 400
|
||||
if len(args) >= 1 and args[0].startswith("POST") and self.path.startswith("/api/") and status_code < 400:
|
||||
return
|
||||
|
||||
super().log_message(format, *args)
|
||||
|
||||
port = int(sys.argv[1]) if "SERVE_DEVELOP" not in environ else 10240
|
||||
server = socketserver.TCPServer(("", port), HTTPHandler)
|
||||
server.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
|
||||
print("[x] started HTTP server.")
|
||||
|
|
|
@ -14,8 +14,9 @@ if [ ! -f "conifg/.env" ]; then
|
|||
fi
|
||||
|
||||
echo "Installation usage:"
|
||||
echo " - ./install.sh \$IP:"
|
||||
echo " Installs Ubuntu Server on \$IP. You will find the correct password in Help > Help on SSH access"
|
||||
echo " - ./install.sh \$CONFIG \$OPTIONAL_IP:"
|
||||
echo " Installs Ubuntu Server using configuration \$CONFIG."
|
||||
echo " \$OPTIONAL_IP is the optional IP address of your computer, if it guesses your IP address wrong."
|
||||
echo
|
||||
echo "Have fun!"
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
# Packages
|
||||
python312Packages.pyyaml
|
||||
python312Packages.termcolor
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
|
|
|
@ -31,6 +31,9 @@ autoinstall:
|
|||
install: false
|
||||
drivers:
|
||||
install: false
|
||||
reporting:
|
||||
hook:
|
||||
type: webhook
|
||||
kernel:
|
||||
package: linux-generic
|
||||
keyboard:
|
||||
|
|
Reference in a new issue