Update.
This commit is contained in:
parent
8e9ee4bff0
commit
f182c2209e
src/snek
@ -8,6 +8,15 @@ import pathlib
|
||||
class ChannelService(BaseService):
|
||||
mapper_name = "channel"
|
||||
|
||||
async def get_home_folder(self, channel_uid):
|
||||
folder = pathlib.Path(f"./drive/{channel_uid}/container/home")
|
||||
if not folder.exists():
|
||||
try:
|
||||
folder.mkdir(parents=True, exist_ok=True)
|
||||
except:
|
||||
pass
|
||||
return folder
|
||||
|
||||
async def get_attachment_folder(self, channel_uid,ensure=False):
|
||||
path = pathlib.Path(f"./drive/{channel_uid}/attachments")
|
||||
if ensure:
|
||||
@ -56,6 +65,7 @@ class ChannelService(BaseService):
|
||||
model["is_private"] = is_private
|
||||
model["is_listed"] = is_listed
|
||||
if await self.save(model):
|
||||
await self.services.container.create(model["uid"])
|
||||
return model
|
||||
raise Exception(f"Failed to create channel: {model.errors}.")
|
||||
|
||||
|
@ -1,9 +1,36 @@
|
||||
from snek.system.service import BaseService
|
||||
from snek.system.docker import ComposeFileManager
|
||||
|
||||
class ContainerService(BaseService):
|
||||
mapper_name = "container"
|
||||
|
||||
async def create(self, id, name, status, resources=None, user_uid=None, path=None, readonly=False):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.compose_path = "snek-container-compose.yml"
|
||||
self.compose = ComposeFileManager(self.compose_path)
|
||||
|
||||
|
||||
|
||||
async def get_instances(self):
|
||||
return list(self.compose.list_instances())
|
||||
|
||||
async def get_container_name(self, channel_uid):
|
||||
return f"channel-{channel_uid}"
|
||||
|
||||
async def create(self, channel_uid, image="ubuntu:latest", command=None, cpus=1, memory='1024m', ports=None, volumes=None):
|
||||
name = await self.get_container_name(channel_uid)
|
||||
self.compose.create_instance(
|
||||
name,
|
||||
image,
|
||||
command,
|
||||
cpus,
|
||||
memory,
|
||||
ports,
|
||||
["./"+str((await self.services.channel.get_home_folder(channel_uid))) +":"+ "/home/ubuntu"]
|
||||
)
|
||||
|
||||
async def create2(self, id, name, status, resources=None, user_uid=None, path=None, readonly=False):
|
||||
model = await self.new()
|
||||
model["id"] = id
|
||||
model["name"] = name
|
||||
|
75
src/snek/system/docker.py
Normal file
75
src/snek/system/docker.py
Normal file
@ -0,0 +1,75 @@
|
||||
import yaml
|
||||
import copy
|
||||
|
||||
class ComposeFileManager:
|
||||
def __init__(self, compose_path='docker-compose.yml'):
|
||||
self.compose_path = compose_path
|
||||
self._load()
|
||||
|
||||
def _load(self):
|
||||
try:
|
||||
with open(self.compose_path, 'r') as f:
|
||||
self.compose = yaml.safe_load(f) or {}
|
||||
except FileNotFoundError:
|
||||
self.compose = {'version': '3', 'services': {}}
|
||||
|
||||
def _save(self):
|
||||
with open(self.compose_path, 'w') as f:
|
||||
yaml.dump(self.compose, f, default_flow_style=False)
|
||||
|
||||
def list_instances(self):
|
||||
return list(self.compose.get('services', {}).keys())
|
||||
|
||||
def create_instance(self, name, image, command=None, cpus=None, memory=None, ports=None, volumes=None):
|
||||
service = {
|
||||
'image': image,
|
||||
}
|
||||
if command:
|
||||
service['command'] = command
|
||||
if cpus or memory:
|
||||
service['deploy'] = {'resources': {'limits': {}}}
|
||||
if cpus:
|
||||
service['deploy']['resources']['limits']['cpus'] = str(cpus)
|
||||
if memory:
|
||||
service['deploy']['resources']['limits']['memory'] = str(memory)
|
||||
if ports:
|
||||
service['ports'] = [f"{host}:{container}" for container, host in ports.items()]
|
||||
if volumes:
|
||||
service['volumes'] = volumes
|
||||
|
||||
self.compose.setdefault('services', {})[name] = service
|
||||
self._save()
|
||||
|
||||
def remove_instance(self, name):
|
||||
if name in self.compose.get('services', {}):
|
||||
del self.compose['services'][name]
|
||||
self._save()
|
||||
|
||||
def get_instance(self, name):
|
||||
return self.compose.get('services', {}).get(name)
|
||||
|
||||
def duplicate_instance(self, name, new_name):
|
||||
orig = self.get_instance(name)
|
||||
if not orig:
|
||||
raise ValueError(f"No such instance: {name}")
|
||||
self.compose['services'][new_name] = copy.deepcopy(orig)
|
||||
self._save()
|
||||
|
||||
def update_instance(self, name, **kwargs):
|
||||
service = self.get_instance(name)
|
||||
if not service:
|
||||
raise ValueError(f"No such instance: {name}")
|
||||
for k, v in kwargs.items():
|
||||
if v is not None:
|
||||
service[k] = v
|
||||
self.compose['services'][name] = service
|
||||
self._save()
|
||||
|
||||
# Storage size is not tracked in compose files; would need Docker API for that.
|
||||
|
||||
# Example usage:
|
||||
# mgr = ComposeFileManager()
|
||||
# mgr.create_instance('web', 'nginx:latest', cpus=1, memory='512m', ports={80:8080}, volumes=['./data:/data'])
|
||||
# print(mgr.list_instances())
|
||||
# mgr.duplicate_instance('web', 'web_copy')
|
||||
# mgr.remove_instance('web_copy')
|
@ -5,7 +5,21 @@
|
||||
{% block main %}
|
||||
|
||||
<section class="chat-area">
|
||||
<message-list class="chat-messages">
|
||||
<message-list class="chat-messages">
|
||||
{% if not messages %}
|
||||
|
||||
<div>
|
||||
<h1>Welcome to your new channel!</h1>
|
||||
<p>This is the start of something great. Use the commands below to get started:</p>
|
||||
<ul>
|
||||
<li>Press <code>/invite</code> to invite someone.</li>
|
||||
<li>Press <code>/online</code> to see who's currently online.</li>
|
||||
<li>Press <code>/help</code> to view all available commands.</li>
|
||||
</ul>
|
||||
<p>Enjoy chatting!</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% for message in messages %}
|
||||
{% autoescape false %}
|
||||
{{ message.html }}
|
||||
|
Loading…
Reference in New Issue
Block a user