mirror of
https://github.com/HuFlungDu/pylibmeshctrl.git
synced 2026-02-20 13:42:11 +00:00
First real commit, everything implemented
This commit is contained in:
108
tests/environment/__init__.py
Normal file
108
tests/environment/__init__.py
Normal file
@@ -0,0 +1,108 @@
|
||||
import os
|
||||
import base64
|
||||
import subprocess
|
||||
import time
|
||||
import json
|
||||
import atexit
|
||||
import pytest
|
||||
import requests
|
||||
thisdir = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
# os.environ["BUILDKIT_PROGRESS"] = "plain"
|
||||
|
||||
|
||||
USERNAMES = ["admin", "privileged", "unprivileged"]
|
||||
global users
|
||||
users = None
|
||||
|
||||
def create_env():
|
||||
global users
|
||||
if users is not None:
|
||||
return users
|
||||
users = {}
|
||||
for username in USERNAMES:
|
||||
password = base64.b64encode(os.urandom(24)).decode()
|
||||
users[username] = password
|
||||
with open(os.path.join(thisdir, "scripts", "meshcentral", "users.json"), "w") as outfile:
|
||||
json.dump(users, outfile)
|
||||
return users
|
||||
|
||||
global _docker_process
|
||||
_docker_process = None
|
||||
|
||||
class Agent(object):
|
||||
def __init__(self, meshid, mcurl, clienturl, dockerurl):
|
||||
self.meshid = meshid
|
||||
self._mcurl = mcurl
|
||||
self._clienturl = clienturl
|
||||
self._dockerurl = dockerurl
|
||||
r = requests.post(f"{self._clienturl}/add-agent", json={"url": f"{self._dockerurl}", "meshid": self.meshid})
|
||||
self.nodeid = r.json()["id"]
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_t, exc_v, exc_tb):
|
||||
try:
|
||||
requests.post("{self._clienturl}/remove-agent/{self.nodeid}")
|
||||
except:
|
||||
pass
|
||||
|
||||
class TestEnvironment(object):
|
||||
def __init__(self):
|
||||
self.users = create_env()
|
||||
self._subp = None
|
||||
self.mcurl = "wss://localhost:8086"
|
||||
self.clienturl = "http://localhost:5000"
|
||||
self._dockerurl = "host.docker.internal:8086"
|
||||
|
||||
def __enter__(self):
|
||||
global _docker_process
|
||||
if _docker_process is not None:
|
||||
self._subp = _docker_process
|
||||
return self
|
||||
self._subp = _docker_process = subprocess.Popen(["docker", "compose", "up", "--build", "--force-recreate", "--no-deps"], stdout=subprocess.DEVNULL, cwd=thisdir)
|
||||
timeout = 30
|
||||
start = time.time()
|
||||
while time.time() - start < timeout:
|
||||
try:
|
||||
data = subprocess.check_output(["docker", "inspect", "meshctrl-meshcentral", "--format='{{json .State.Health}}'"], cwd=thisdir, stderr=subprocess.DEVNULL)
|
||||
# docker outputs for humans, not computers. This is the easiest way to chop off the ends
|
||||
data = json.loads(data.strip()[1:-1])
|
||||
except Exception as e:
|
||||
time.sleep(1)
|
||||
continue
|
||||
try:
|
||||
if data["Status"] == "healthy":
|
||||
break
|
||||
except:
|
||||
pass
|
||||
time.sleep(1)
|
||||
else:
|
||||
self.__exit__(None, None, None)
|
||||
raise Exception("Failed to create docker instance")
|
||||
return self
|
||||
|
||||
|
||||
def __exit__(self, exc_t, exc_v, exc_tb):
|
||||
pass
|
||||
|
||||
def create_agent(self, meshid):
|
||||
return Agent(meshid, self.mcurl, self.clienturl, self._dockerurl)
|
||||
|
||||
def _kill_docker_process():
|
||||
if _docker_process is not None:
|
||||
_docker_process.kill()
|
||||
subprocess.run(["docker", "compose", "down"], cwd=thisdir)
|
||||
|
||||
atexit.register(_kill_docker_process)
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def env():
|
||||
with TestEnvironment() as e:
|
||||
yield e
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with TestEnvironment() as env:
|
||||
input("it's up")
|
||||
18
tests/environment/client.dockerfile
Normal file
18
tests/environment/client.dockerfile
Normal file
@@ -0,0 +1,18 @@
|
||||
FROM python:3.12
|
||||
WORKDIR /usr/local/app
|
||||
|
||||
# Install the application dependencies
|
||||
# COPY requirements.txt ./
|
||||
|
||||
|
||||
# Copy in the source code
|
||||
COPY scripts/client ./scripts
|
||||
RUN pip install --no-cache-dir -r ./scripts/requirements.txt
|
||||
EXPOSE 5000
|
||||
|
||||
# Setup an app user so the container doesn't run as the root user
|
||||
RUN useradd app
|
||||
USER app
|
||||
WORKDIR /usr/local/app/scripts
|
||||
|
||||
CMD ["python3", "-m", "flask", "--app", "agent_server", "run", "--host=0.0.0.0", "--debug"]
|
||||
52
tests/environment/compose.yaml
Normal file
52
tests/environment/compose.yaml
Normal file
@@ -0,0 +1,52 @@
|
||||
networks:
|
||||
meshctrl:
|
||||
driver: bridge
|
||||
|
||||
services:
|
||||
client:
|
||||
restart: unless-stopped
|
||||
container_name: meshctrl-client
|
||||
image: client
|
||||
build:
|
||||
dockerfile: client.dockerfile
|
||||
ports:
|
||||
- 5000:5000
|
||||
depends_on:
|
||||
- meshcentral
|
||||
environment:
|
||||
TZ: US/LosAngeles
|
||||
# volumes:
|
||||
# # mongodb data-directory - A must for data persistence
|
||||
# - ./meshcentral/mongodb_data:/data/db
|
||||
networks:
|
||||
- meshctrl
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
|
||||
meshcentral:
|
||||
restart: always
|
||||
container_name: meshctrl-meshcentral
|
||||
# use the official meshcentral container
|
||||
image: meshcentral
|
||||
build:
|
||||
dockerfile: meshcentral.dockerfile
|
||||
ports:
|
||||
# MeshCentral will moan and try everything not to use port 80, but you can also use it if you so desire, just change the config.json according to your needs
|
||||
- 8086:443
|
||||
environment:
|
||||
TZ: US/LosAngeles
|
||||
#volumes:
|
||||
# config.json and other important files live here. A must for data persistence
|
||||
#- ./meshcentral/data:/opt/meshcentral/meshcentral-data
|
||||
# where file uploads for users live
|
||||
#- ./meshcentral/user_files:/opt/meshcentral/meshcentral-files
|
||||
# location for the meshcentral-backups - this should be mounted to an external storage
|
||||
#- ./meshcentral/backup:/opt/meshcentral/meshcentral-backups
|
||||
# location for site customization files
|
||||
#- ./meshcentral/web:/opt/meshcentral/meshcentral-web
|
||||
networks:
|
||||
- meshctrl
|
||||
healthcheck:
|
||||
test: curl -k --fail https://localhost:443/ || exit 1
|
||||
interval: 5s
|
||||
timeout: 120s
|
||||
7
tests/environment/meshcentral.dockerfile
Normal file
7
tests/environment/meshcentral.dockerfile
Normal file
@@ -0,0 +1,7 @@
|
||||
FROM ghcr.io/ylianst/meshcentral:latest
|
||||
RUN apk add curl
|
||||
RUN apk add python3
|
||||
WORKDIR /opt/meshcentral/
|
||||
COPY ./scripts/meshcentral ./scripts
|
||||
COPY ./meshcentral/data /opt/meshcentral/meshcentral-data
|
||||
CMD ["python3", "/opt/meshcentral/scripts/create_users.py"]
|
||||
38
tests/environment/meshcentral/data/config.json
Normal file
38
tests/environment/meshcentral/data/config.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/Ylianst/MeshCentral/master/meshcentral-config-schema.json",
|
||||
"settings": {
|
||||
"plugins":{"enabled": false},
|
||||
"_mongoDb": null,
|
||||
"cert": "host.docker.internal",
|
||||
"_WANonly": true,
|
||||
"_LANonly": true,
|
||||
"port": 443,
|
||||
"aliasPort": 8086,
|
||||
"redirPort": 80,
|
||||
"_redirAliasPort": 80,
|
||||
"AgentPong": 300,
|
||||
"TLSOffload": false,
|
||||
"SelfUpdate": false,
|
||||
"AllowFraming": false,
|
||||
"WebRTC": false,
|
||||
"interUserMessaging": true
|
||||
},
|
||||
"domains": {
|
||||
"": {
|
||||
"_title": "MyServer",
|
||||
"_title2": "Servername",
|
||||
"minify": true,
|
||||
"NewAccounts": false,
|
||||
"localSessionRecording": false,
|
||||
"_userNameIsEmail": true,
|
||||
"_certUrl": "my.reverse.proxy",
|
||||
"allowedOrigin": true
|
||||
}
|
||||
},
|
||||
"_letsencrypt": {
|
||||
"__comment__": "Requires NodeJS 8.x or better, Go to https://letsdebug.net/ first before>",
|
||||
"_email": "myemail@mydomain.com",
|
||||
"_names": "myserver.mydomain.com",
|
||||
"production": false
|
||||
}
|
||||
}
|
||||
68
tests/environment/scripts/client/agent_server.py
Normal file
68
tests/environment/scripts/client/agent_server.py
Normal file
@@ -0,0 +1,68 @@
|
||||
from flask import Flask, json, request
|
||||
import requests
|
||||
import tempfile
|
||||
import base64
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
AGENT_URL_TEMPLATE = "https://{}/meshagents?id=6"
|
||||
SETTINGS_URL_TEMPLATE = "https://{}/meshsettings?id={}"
|
||||
|
||||
api = Flask(__name__)
|
||||
|
||||
agents = {}
|
||||
|
||||
# if not os.path.exists(os.path.join(mesh_dir, "meshagent")):
|
||||
# os.makedirs(meshtemp)
|
||||
# subprocess.check_call(["wget", AGENT_URL, "-O", os.path.join(meshtemp, "meshagent")])
|
||||
# subprocess.check_call(["wget", SETTINGS_URL, "-O", os.path.join(meshtemp, "meshagent.msh")])
|
||||
# subprocess.check_call(["chmod", "+x", os.path.join(meshtemp, "meshagent")])
|
||||
# shutil.copytree(meshtemp, mesh_dir)
|
||||
# subprocess.check_call(["chown", "-R", f"{user}:{user}", mesh_dir])
|
||||
|
||||
@api.route('/add-agent', methods=['POST'])
|
||||
def add_agent():
|
||||
api.logger.info("text")
|
||||
AGENT_URL = AGENT_URL_TEMPLATE.format(request.json["url"])
|
||||
SETTINGS_URL = SETTINGS_URL_TEMPLATE.format(request.json["url"], request.json["meshid"])
|
||||
d = tempfile.mkdtemp()
|
||||
agent_path = os.path.join(d, "meshagent")
|
||||
msh_path = os.path.join(d, "meshagent.msh")
|
||||
with open(agent_path, "wb") as outfile:
|
||||
for chunk in requests.get(AGENT_URL, stream=True, verify=False).iter_content(chunk_size=16*1024):
|
||||
outfile.write(chunk)
|
||||
with open(msh_path, "wb") as outfile:
|
||||
for chunk in requests.get(SETTINGS_URL, stream=True, verify=False).iter_content(chunk_size=16*1024):
|
||||
outfile.write(chunk)
|
||||
os.chmod(agent_path, 0o0777)
|
||||
os.chmod(msh_path, 0o0777)
|
||||
# Generates a certificate if we don't got one
|
||||
subprocess.call([agent_path, "-connect"])
|
||||
agent_hex = subprocess.check_output([agent_path, '-exec', "console.log(require('_agentNodeId')());process.exit()"]).strip().decode()
|
||||
agent_id = base64.b64encode(bytes.fromhex(agent_hex)).decode().replace("+", "@").replace("/", "$")
|
||||
p = subprocess.Popen(["stdbuf", "-o0", agent_path, "connect"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=d)
|
||||
agents[agent_id] = {"process": p, "id": agent_id}
|
||||
text = ""
|
||||
start = time.time()
|
||||
while time.time() - start < 5:
|
||||
text += p.stdout.read1().decode("utf-8")
|
||||
api.logger.info(text)
|
||||
if "Connected." in text:
|
||||
break
|
||||
time.sleep(.1)
|
||||
else:
|
||||
raise Exception(f"Failed to start agent: {text}")
|
||||
return {"id": agent_id}
|
||||
|
||||
@api.route('/remove-agent/<agentid>', methods=['POST'])
|
||||
def remove_agent(agentid):
|
||||
agents[agentid]["process"].kill()
|
||||
return ""
|
||||
|
||||
@api.route('/', methods=['GET'])
|
||||
def slash():
|
||||
return [_["id"] for _ in agents]
|
||||
|
||||
if __name__ == '__main__':
|
||||
api.run()
|
||||
2
tests/environment/scripts/client/requirements.txt
Normal file
2
tests/environment/scripts/client/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
flask
|
||||
requests
|
||||
15
tests/environment/scripts/meshcentral/create_users.py
Normal file
15
tests/environment/scripts/meshcentral/create_users.py
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import json
|
||||
thisdir = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
with open(os.path.join(thisdir, "users.json")) as infile:
|
||||
users = json.load(infile)
|
||||
for username, password in users.items():
|
||||
subprocess.check_output(["node", "/opt/meshcentral/meshcentral", "--createaccount", username, "--pass", password, "--name", username])
|
||||
|
||||
|
||||
subprocess.check_output(["node", "/opt/meshcentral/meshcentral", "--adminaccount", "admin"])
|
||||
|
||||
subprocess.call(["bash", "/opt/meshcentral/startup.sh"])
|
||||
1
tests/environment/scripts/meshcentral/users.json
Normal file
1
tests/environment/scripts/meshcentral/users.json
Normal file
@@ -0,0 +1 @@
|
||||
{"admin": "3U6zP4iIes5ISH15XxjYLjJcCdw9jU0m", "privileged": "aiIO0zLMGsU7++FYVDNxhlpYlZ1andRB", "unprivileged": "Cz9OMV1wkVd9pXdWi4lkBAAu6TMt43MA"}
|
||||
Reference in New Issue
Block a user