minor updates and fixes
code written so smooth, that butter takes notes
This commit is contained in:
parent
b25eb51ff0
commit
a27f14f036
9 changed files with 86835 additions and 8 deletions
55
simple/app.py
Normal file
55
simple/app.py
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
from flask import Flask, request, jsonify
|
||||||
|
import requests
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
# Define the base port for the target servers
|
||||||
|
BASE_PORT = 8000
|
||||||
|
|
||||||
|
@app.route('/', defaults={'path': ''}, methods=["GET", "POST", "PUT", "DELETE", "PATCH"])
|
||||||
|
@app.route('/<path:path>', methods=["GET", "POST", "PUT", "DELETE", "PATCH"])
|
||||||
|
def proxy(path):
|
||||||
|
# Split the path into segments
|
||||||
|
path_segments = path.strip('/').split('/')
|
||||||
|
|
||||||
|
# The first segment should be the IPv6 address
|
||||||
|
if not path_segments:
|
||||||
|
return jsonify({"error": "No path provided"}), 400
|
||||||
|
|
||||||
|
ipv6_address = path_segments[0]
|
||||||
|
|
||||||
|
# Construct the target URL using the extracted IPv6 address
|
||||||
|
target_url = f"http://[{ipv6_address}]:{BASE_PORT}"
|
||||||
|
|
||||||
|
# Reconstruct the remaining path (if any)
|
||||||
|
remaining_path = '/'.join(path_segments[1:])
|
||||||
|
|
||||||
|
# Construct the full URL to proxy to
|
||||||
|
full_url = f"{target_url}/{remaining_path}" if remaining_path else target_url
|
||||||
|
|
||||||
|
# Forward the request to the target server
|
||||||
|
try:
|
||||||
|
response = requests.request(
|
||||||
|
method=request.method,
|
||||||
|
url=full_url,
|
||||||
|
headers={key: value for (key, value) in request.headers if key != "Host"},
|
||||||
|
data=request.get_data(),
|
||||||
|
cookies=request.cookies,
|
||||||
|
allow_redirects=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Return the response from the target server
|
||||||
|
return (response.content, response.status_code, response.headers.items())
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
stuff = {
|
||||||
|
"method": request.method,
|
||||||
|
"url": full_url,
|
||||||
|
"headers": {key: value for (key, value) in request.headers if key != "Host"},
|
||||||
|
"data": request.get_data(),
|
||||||
|
"cookies": request.cookies,
|
||||||
|
"error": str(e)
|
||||||
|
}
|
||||||
|
return jsonify(str(stuff)), 500
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run(host="0.0.0.0", port=8000) # Listen on all interfaces
|
||||||
17
simple/dockerfile
Normal file
17
simple/dockerfile
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Use an official Python base image
|
||||||
|
FROM python:3.11
|
||||||
|
|
||||||
|
# Set the working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install Jupyter
|
||||||
|
RUN pip install gunicorn requests flask jsonify
|
||||||
|
|
||||||
|
# Copy the notebook file into the container
|
||||||
|
COPY app.py /app/
|
||||||
|
|
||||||
|
# Expose port 8000
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# Command to execute the notebook directly
|
||||||
|
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]
|
||||||
17
testing/dockerfile
Normal file
17
testing/dockerfile
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Use an official Python base image
|
||||||
|
FROM python:3.11
|
||||||
|
|
||||||
|
# Set the working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install Jupyter
|
||||||
|
RUN pip install --no-cache-dir jupyter
|
||||||
|
|
||||||
|
# Copy the notebook file into the container
|
||||||
|
COPY notebook.ipynb /app/
|
||||||
|
|
||||||
|
# Expose port 8000
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# Command to execute the notebook directly
|
||||||
|
CMD ["jupyter", "execute", "notebook.ipynb"]
|
||||||
86331
testing/notebook.ipynb
Normal file
86331
testing/notebook.ipynb
Normal file
File diff suppressed because it is too large
Load diff
89
testing/test.json
Normal file
89
testing/test.json
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
{
|
||||||
|
"id": "e28603db425378",
|
||||||
|
"name": "purple-surf-8738",
|
||||||
|
"state": "stopped",
|
||||||
|
"region": "sea",
|
||||||
|
"instance_id": "01JNCJEMDYBD351S6JFZF316V0",
|
||||||
|
"private_ip": "fdaa:c:cd38:a7b:bbfb:6619:1264:2",
|
||||||
|
"config": {
|
||||||
|
"env": {
|
||||||
|
"FLY_PROCESS_GROUP": "app"
|
||||||
|
},
|
||||||
|
"init": {},
|
||||||
|
"guest": {
|
||||||
|
"cpu_kind": "shared",
|
||||||
|
"cpus": 1,
|
||||||
|
"memory_mb": 512
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"fly_flyctl_version": "0.3.87",
|
||||||
|
"fly_platform_version": "v2",
|
||||||
|
"fly_process_group": "app",
|
||||||
|
"fly_release_id": "7pGPxgX11jYG2T94NqvJGYYxB",
|
||||||
|
"fly_release_version": "1"
|
||||||
|
},
|
||||||
|
"services": [
|
||||||
|
{
|
||||||
|
"protocol": "tcp",
|
||||||
|
"internal_port": 8000,
|
||||||
|
"force_instance_key": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"image": "registry.fly.io/snaketest:snake@sha256:5bba2930e42eeb25bb8128bacb61665be3d919a91dad60ac83420b7ffd18ea4b",
|
||||||
|
"restart": {
|
||||||
|
"policy": "on-failure",
|
||||||
|
"max_retries": 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"incomplete_config": null,
|
||||||
|
"image_ref": {
|
||||||
|
"registry": "registry.fly.io",
|
||||||
|
"repository": "snaketest",
|
||||||
|
"tag": "snake",
|
||||||
|
"digest": "sha256:5bba2930e42eeb25bb8128bacb61665be3d919a91dad60ac83420b7ffd18ea4b",
|
||||||
|
"labels": null
|
||||||
|
},
|
||||||
|
"created_at": "2025-03-02T19:21:57Z",
|
||||||
|
"updated_at": "2025-03-03T00:13:15Z",
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"id": "01JNCNK2EXW38075F1Q6N7RRNZ",
|
||||||
|
"type": "stop",
|
||||||
|
"status": "stopped",
|
||||||
|
"source": "user",
|
||||||
|
"timestamp": 1740960795101
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "01JNCNJZ4RGAV7FZNMMPTMDBEH",
|
||||||
|
"type": "suspension",
|
||||||
|
"status": "suspended",
|
||||||
|
"source": "flyd",
|
||||||
|
"timestamp": 1740960791704
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "01JNCNJWBKGY0AVF1PGTB0PT1N",
|
||||||
|
"type": "suspension",
|
||||||
|
"status": "suspending",
|
||||||
|
"source": "user",
|
||||||
|
"timestamp": 1740960788851
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "01JNCJET67K8AC0SQ06HDVZ15X",
|
||||||
|
"type": "start",
|
||||||
|
"status": "started",
|
||||||
|
"request": {
|
||||||
|
"gpu_spot_price": 0
|
||||||
|
},
|
||||||
|
"source": "flyd",
|
||||||
|
"timestamp": 1740957509831
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "01JNCJEMEAK0D58J30VJ6V5000",
|
||||||
|
"type": "launch",
|
||||||
|
"status": "created",
|
||||||
|
"source": "user",
|
||||||
|
"timestamp": 1740957503946
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"host_status": "ok"
|
||||||
|
}
|
||||||
17
uploader-service/Dockerfile
Normal file
17
uploader-service/Dockerfile
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Use an official Python base image
|
||||||
|
FROM python:3.11
|
||||||
|
|
||||||
|
# Set the working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install Jupyter
|
||||||
|
RUN pip install --no-cache-dir jupyter
|
||||||
|
|
||||||
|
# Copy the notebook file into the container
|
||||||
|
COPY notebook.ipynb /app/
|
||||||
|
|
||||||
|
# Expose port 8000
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# Command to execute the notebook directly
|
||||||
|
CMD ["jupyter", "execute", "notebook.ipynb"]
|
||||||
207
uploader-service/app.py
Normal file
207
uploader-service/app.py
Normal file
|
|
@ -0,0 +1,207 @@
|
||||||
|
import os
|
||||||
|
import uuid
|
||||||
|
import sqlite3
|
||||||
|
import docker
|
||||||
|
import requests
|
||||||
|
import threading
|
||||||
|
from flask import Flask, request, jsonify, render_template
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import shutil
|
||||||
|
import dotenv
|
||||||
|
dotenv.load_dotenv()
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
DATABASE = "machines.db"
|
||||||
|
FLY_APP = "snaketest"
|
||||||
|
FLY_REGISTRY = os.getenv("FLY_REGISTRY")
|
||||||
|
FLY_API_URL = os.getenv("FLY_API_URL")
|
||||||
|
FLY_API_TOKEN = os.getenv("FLY_API_TOKEN")
|
||||||
|
|
||||||
|
# Ensure database exists
|
||||||
|
def init_db():
|
||||||
|
with sqlite3.connect(DATABASE) as conn:
|
||||||
|
conn.execute('''CREATE TABLE IF NOT EXISTS machines (
|
||||||
|
name TEXT PRIMARY KEY,
|
||||||
|
pid INTEGER,
|
||||||
|
ipv6 TEXT
|
||||||
|
|
||||||
|
)''')
|
||||||
|
|
||||||
|
ready_to_deploy = False
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/upload", methods=["POST"])
|
||||||
|
def upload_file():
|
||||||
|
if "file" not in request.files:
|
||||||
|
return jsonify({"error": "No IPython Notebook file uploaded"}), 400
|
||||||
|
|
||||||
|
file = request.files["file"]
|
||||||
|
json_file = request.files.get("json_file")
|
||||||
|
|
||||||
|
name = request.form.get("name")
|
||||||
|
if not name:
|
||||||
|
return jsonify({"error": "Name is required"}), 400
|
||||||
|
# check if name already taken
|
||||||
|
with sqlite3.connect(DATABASE) as conn:
|
||||||
|
cur = conn.cursor()
|
||||||
|
cur.execute("SELECT * FROM machines WHERE name = ?", (name,))
|
||||||
|
if cur.fetchone():
|
||||||
|
return jsonify({"error": "Name already taken"}), 409
|
||||||
|
|
||||||
|
pid = request.form.get("pid")
|
||||||
|
|
||||||
|
unique_id = str(uuid.uuid4())
|
||||||
|
|
||||||
|
upload_dir = os.path.join("uploads", unique_id)
|
||||||
|
os.makedirs(upload_dir, exist_ok=True)
|
||||||
|
|
||||||
|
file_path = os.path.join(upload_dir, "notebook.ipynb")
|
||||||
|
file.save(file_path)
|
||||||
|
|
||||||
|
if json_file:
|
||||||
|
json_path = os.path.join(upload_dir, json_file.filename)
|
||||||
|
json_file.save(json_path)
|
||||||
|
|
||||||
|
os.system(f'cp Dockerfile {upload_dir}/Dockerfile')
|
||||||
|
|
||||||
|
|
||||||
|
# threading.Thread(target=build_and_push_docker_image, args=(name, pid, unique_id, upload_dir)).start()
|
||||||
|
ipv6 = build_and_push_docker_image(name, pid, unique_id, upload_dir)
|
||||||
|
|
||||||
|
if ipv6 is None:
|
||||||
|
return jsonify({"error": "Failed to deploy this snake"}), 500
|
||||||
|
|
||||||
|
print("ipv6", ipv6)
|
||||||
|
return jsonify({"message": "File uploaded", "ipv6": ipv6}), 200
|
||||||
|
|
||||||
|
def build_and_push_docker_image(name, pid, unique_id, upload_dir):
|
||||||
|
print("Building and pushing docker image")
|
||||||
|
client = docker.from_env()
|
||||||
|
image_tag = f"{FLY_REGISTRY}:{unique_id}"
|
||||||
|
|
||||||
|
|
||||||
|
client.images.build(path=upload_dir, tag=image_tag, dockerfile="Dockerfile")
|
||||||
|
|
||||||
|
|
||||||
|
client.images.push(image_tag)
|
||||||
|
|
||||||
|
print("Docker image built and pushed")
|
||||||
|
global ready_to_deploy
|
||||||
|
ready_to_deploy = True
|
||||||
|
print("about to deploy", ready_to_deploy)
|
||||||
|
ipv6 = deploy_machine(name, pid, image_tag)
|
||||||
|
|
||||||
|
# Cleanup junk
|
||||||
|
# os.system(f"rm -rf {upload_dir}")
|
||||||
|
print("upload", upload_dir)
|
||||||
|
shutil.rmtree(upload_dir)
|
||||||
|
print("Cleanup done")
|
||||||
|
return ipv6
|
||||||
|
|
||||||
|
def deploy_machine(name, pid, image_tag):
|
||||||
|
print("Deploying machine")
|
||||||
|
print("ready to deploy", ready_to_deploy)
|
||||||
|
while not ready_to_deploy:
|
||||||
|
print("sleeping")
|
||||||
|
time.sleep(2)
|
||||||
|
headers = {
|
||||||
|
"Authorization": f"Bearer {FLY_API_TOKEN}",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
data = {
|
||||||
|
"config": {
|
||||||
|
"env": {
|
||||||
|
"FLY_PROCESS_GROUP": "app"
|
||||||
|
},
|
||||||
|
"init": {},
|
||||||
|
"guest": {
|
||||||
|
"cpu_kind": "shared",
|
||||||
|
"cpus": 1,
|
||||||
|
"memory_mb": 512
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"fly_flyctl_version": "0.3.87",
|
||||||
|
"fly_platform_version": "v2",
|
||||||
|
"fly_process_group": "app",
|
||||||
|
"fly_release_id": "7pGPxgX11jYG2T94NqvJGYYxB",
|
||||||
|
"fly_release_version": "1"
|
||||||
|
},
|
||||||
|
"services": [
|
||||||
|
{
|
||||||
|
"protocol": "tcp",
|
||||||
|
"internal_port": 8000,
|
||||||
|
"force_instance_key": None
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"image": f"{image_tag}",
|
||||||
|
"restart": {
|
||||||
|
"policy": "on-failure",
|
||||||
|
"max_retries": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# data = json.dumps(data)
|
||||||
|
response = requests.post(FLY_API_URL, json=data, headers=headers)
|
||||||
|
print(response.json())
|
||||||
|
print(response.status_code)
|
||||||
|
if response.status_code == 200:
|
||||||
|
machine_data = response.json()
|
||||||
|
# fly_machine_id = machine_data["id"]
|
||||||
|
ipv6 = machine_data["private_ip"]
|
||||||
|
print("ipv6", ipv6)
|
||||||
|
with sqlite3.connect(DATABASE) as conn:
|
||||||
|
conn.execute("INSERT INTO machines (name, pid, ipv6) VALUES (?, ?, ?)",
|
||||||
|
(name, pid, ipv6))
|
||||||
|
print("Machine deployed")
|
||||||
|
print(f"Machine deployed with Ip: {ipv6}")
|
||||||
|
return ipv6
|
||||||
|
else:
|
||||||
|
print("Failed to deploy machine.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def upload_to_battlesnake():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_machine_info(machine_id):
|
||||||
|
with sqlite3.connect(DATABASE) as conn:
|
||||||
|
cur = conn.cursor()
|
||||||
|
cur.execute("SELECT pid, ipv6 FROM machines WHERE name = ?", (name))
|
||||||
|
row = cur.fetchone()
|
||||||
|
if row:
|
||||||
|
return {"ipv6": row[0], "status": row[1]}
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/mock", methods=["POST"])
|
||||||
|
def mock():
|
||||||
|
time.sleep(5)
|
||||||
|
return jsonify({"message": "File uploaded", "ipv6": "ff:00:00:00:00::ff"}), 200
|
||||||
|
|
||||||
|
@app.route("/uploader")
|
||||||
|
def uploader():
|
||||||
|
return render_template("uploader.html")
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/status/<machine_id>", methods=["GET"])
|
||||||
|
def status(machine_id):
|
||||||
|
result = get_machine_info(machine_id)
|
||||||
|
if result is None:
|
||||||
|
return jsonify({"error": "Machine not found"}), 404
|
||||||
|
return jsonify(result), 200
|
||||||
|
|
||||||
|
|
||||||
|
# return jsonify({"step": get_step(machine_id), **get_machine_status(machine_id)})
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
init_db()
|
||||||
|
app.run(debug=True, port=5000, host='0.0.0.0')
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
# uploader microservice
|
# docker commands to know
|
||||||
- handle uploads and registration to:
|
|
||||||
1. fly vms with snakes and brains(json)
|
`sudo docker build . -t registry.fly.io/snaketest:uniqueid`
|
||||||
2. google drive folders
|
|
||||||
3. google sheet link population
|
`docker push registry.fly.io/snaketest:uniqueid`
|
||||||
4. battlesnake.com for snake registration with our links
|
|
||||||
- handle backend logic for multiple uploads, errors, reuploads, etc
|
`fly machine update e82d4d9c690e08 --image registry.fly.io/snaketest:uniqueid`
|
||||||
- connect with appscript or sheets api for autopopulation
|
|
||||||
95
uploader-service/templates/uploader.html
Normal file
95
uploader-service/templates/uploader.html
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Notebook uploader</title>
|
||||||
|
<link href="https://unpkg.com/onedivloaders@1.0.0/index.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="container">
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<h1>Byte camp battlesnake uploader</h1>
|
||||||
|
<form id="uploadForm">
|
||||||
|
<label for="name">Name</label>
|
||||||
|
<input type="text" id="name" name="name" placeholder="Enter Name" required>
|
||||||
|
<label for="pid">pid</label>
|
||||||
|
<input type="number" id="email" name="pid" placeholder="Enter pid" required>
|
||||||
|
<label for="notebook">Notebook</label>
|
||||||
|
<input type="file" id="notebook" name="file" required accept=".ipynb">
|
||||||
|
<label for="jsonFile">JSON File (optional)</label>
|
||||||
|
<input type="file" id="jsonFile" name="jsonFile" accept=".json">
|
||||||
|
|
||||||
|
<button type="submit">Upload</button>
|
||||||
|
</form>
|
||||||
|
<style>
|
||||||
|
.message {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.success {
|
||||||
|
background-color: #d4edda;
|
||||||
|
color: #155724;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
background-color: #f8d7da;
|
||||||
|
color: #721c24;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class="message" id="message"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const uploadForm = document.getElementById('uploadForm');
|
||||||
|
const message = document.getElementById('message');
|
||||||
|
|
||||||
|
uploadForm.addEventListener('submit', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
console.log('Form submitted'); // Debugging line
|
||||||
|
const formData = new FormData(uploadForm);
|
||||||
|
|
||||||
|
console.log(formData);
|
||||||
|
console.log('Form submitted'); // Debugging line
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
message.className = "message circle-packman-1";
|
||||||
|
console.log("waiting 5 seconds");
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||||
|
console.log('Form submitted'); // Debugging line
|
||||||
|
const response = await fetch('http://localhost:5000/upload', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
|
||||||
|
if (data.message) {
|
||||||
|
message.className = 'message success';
|
||||||
|
message.textContent = data.message;
|
||||||
|
} else {
|
||||||
|
message.className = 'message error';
|
||||||
|
message.textContent = data.error;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
message.className = 'message error';
|
||||||
|
console.error(error);
|
||||||
|
message.textContent = 'An error occurred while uploading the notebook';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue