Compare commits
18 Commits
mock-hub-c
...
1f384f34b5
Author | SHA1 | Date | |
---|---|---|---|
1f384f34b5 | |||
c25d85bbdd | |||
0f4ac8e444 | |||
3cf501a393 | |||
180efa01af | |||
f5c079ffc2 | |||
0e5dfe6bde | |||
2adbb8d94c | |||
8446d11720 | |||
a580b04659 | |||
2e6894ad14 | |||
2e6c6517f3 | |||
be6c1b38b7 | |||
aa8e129913 | |||
71e09807a7 | |||
4816170c03 | |||
6af241e8be | |||
c8ec53f207 |
14
.drone.yml
Normal file
14
.drone.yml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: publish docker image
|
||||||
|
steps:
|
||||||
|
- name: build and publish
|
||||||
|
image: plugins/docker
|
||||||
|
settings:
|
||||||
|
username:
|
||||||
|
from_secret: docker_reg_username_3wc
|
||||||
|
password:
|
||||||
|
from_secret: docker_reg_passwd_3wc
|
||||||
|
repo: 3wordchant/capsul-flask
|
||||||
|
tags: latest
|
||||||
|
|
48
Dockerfile
Normal file
48
Dockerfile
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
FROM python:3.8-alpine as build
|
||||||
|
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
build-base \
|
||||||
|
gcc \
|
||||||
|
gettext \
|
||||||
|
git \
|
||||||
|
jpeg-dev \
|
||||||
|
libffi-dev \
|
||||||
|
libjpeg \
|
||||||
|
musl-dev \
|
||||||
|
postgresql-dev \
|
||||||
|
python3-dev \
|
||||||
|
zlib-dev
|
||||||
|
|
||||||
|
RUN mkdir -p /app/{code,venv}
|
||||||
|
WORKDIR /app/code
|
||||||
|
COPY Pipfile Pipfile.lock /app/code/
|
||||||
|
|
||||||
|
RUN python3 -m venv /app/venv
|
||||||
|
RUN pip install pipenv setuptools
|
||||||
|
ENV PATH="/app/venv/bin:$PATH" VIRTUAL_ENV="/app/venv"
|
||||||
|
RUN pip install wheel cppy
|
||||||
|
# Install dependencies into the virtual environment with Pipenv
|
||||||
|
RUN pipenv install --deploy --verbose
|
||||||
|
|
||||||
|
FROM python:3.8-alpine
|
||||||
|
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
cloud-utils \
|
||||||
|
libjpeg \
|
||||||
|
libpq \
|
||||||
|
libstdc++ \
|
||||||
|
libvirt-client \
|
||||||
|
openssh-client \
|
||||||
|
virt-install
|
||||||
|
|
||||||
|
COPY . /app/code/
|
||||||
|
WORKDIR /app/code
|
||||||
|
|
||||||
|
COPY --from=build /app/venv /app/venv
|
||||||
|
ENV PATH="/app/venv/bin:$PATH" VIRTUAL_ENV="/app/venv"
|
||||||
|
|
||||||
|
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "-k", "gevent", "--worker-connections", "1000", "app:app"]
|
||||||
|
|
||||||
|
VOLUME /app/code
|
||||||
|
|
||||||
|
EXPOSE 5000
|
@ -26,8 +26,24 @@ class StdoutMockFlaskMail:
|
|||||||
def send(self, message: Message):
|
def send(self, message: Message):
|
||||||
current_app.logger.info(f"Email would have been sent if configured:\n\nto: {','.join(message.recipients)}\nsubject: {message.subject}\nbody:\n\n{message.body}\n\n")
|
current_app.logger.info(f"Email would have been sent if configured:\n\nto: {','.join(message.recipients)}\nsubject: {message.subject}\nbody:\n\n{message.body}\n\n")
|
||||||
|
|
||||||
|
|
||||||
load_dotenv(find_dotenv())
|
load_dotenv(find_dotenv())
|
||||||
|
|
||||||
|
for var_name in [
|
||||||
|
"SPOKE_HOST_TOKEN", "HUB_TOKEN", "STRIPE_SECRET_KEY",
|
||||||
|
"BTCPAY_PRIVATE_KEY", "MAIL_PASSWORD"
|
||||||
|
]:
|
||||||
|
var = os.environ.get(f"{var_name}_FILE")
|
||||||
|
if not var:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not os.path.isfile(var):
|
||||||
|
continue
|
||||||
|
|
||||||
|
with open(var) as secret_file:
|
||||||
|
os.environ[var_name] = secret_file.read().rstrip('\n')
|
||||||
|
del os.environ[f"{var_name}_FILE"]
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
app.config.from_mapping(
|
app.config.from_mapping(
|
||||||
@ -71,7 +87,7 @@ app.config.from_mapping(
|
|||||||
#STRIPE_WEBHOOK_SECRET=os.environ.get("STRIPE_WEBHOOK_SECRET", default="")
|
#STRIPE_WEBHOOK_SECRET=os.environ.get("STRIPE_WEBHOOK_SECRET", default="")
|
||||||
|
|
||||||
BTCPAY_PRIVATE_KEY=os.environ.get("BTCPAY_PRIVATE_KEY", default="").replace("\\n", "\n"),
|
BTCPAY_PRIVATE_KEY=os.environ.get("BTCPAY_PRIVATE_KEY", default="").replace("\\n", "\n"),
|
||||||
BTCPAY_URL=os.environ.get("BTCPAY_URL", default="")
|
BTCPAY_URL=os.environ.get("BTCPAY_URL", default="https://btcpay.cyberia.club")
|
||||||
)
|
)
|
||||||
|
|
||||||
app.config['HUB_URL'] = os.environ.get("HUB_URL", default=app.config['BASE_URL'])
|
app.config['HUB_URL'] = os.environ.get("HUB_URL", default=app.config['BASE_URL'])
|
||||||
@ -139,13 +155,10 @@ else:
|
|||||||
|
|
||||||
app.config['HTTP_CLIENT'] = MyHTTPClient(timeout_seconds=int(app.config['INTERNAL_HTTP_TIMEOUT_SECONDS']))
|
app.config['HTTP_CLIENT'] = MyHTTPClient(timeout_seconds=int(app.config['INTERNAL_HTTP_TIMEOUT_SECONDS']))
|
||||||
|
|
||||||
app.config['BTCPAY_ENABLED'] = False
|
try:
|
||||||
if app.config['BTCPAY_URL'] != "":
|
app.config['BTCPAY_CLIENT'] = btcpay.Client(api_uri=app.config['BTCPAY_URL'], pem=app.config['BTCPAY_PRIVATE_KEY'])
|
||||||
try:
|
except:
|
||||||
app.config['BTCPAY_CLIENT'] = btcpay.Client(api_uri=app.config['BTCPAY_URL'], pem=app.config['BTCPAY_PRIVATE_KEY'])
|
app.logger.warning("unable to create btcpay client. Capsul will work fine except cryptocurrency payments will not work. The error was: " + my_exec_info_message(sys.exc_info()))
|
||||||
app.config['BTCPAY_ENABLED'] = True
|
|
||||||
except:
|
|
||||||
app.logger.warning("unable to create btcpay client. Capsul will work fine except cryptocurrency payments will not work. The error was: " + my_exec_info_message(sys.exc_info()))
|
|
||||||
|
|
||||||
# only start the scheduler and attempt to migrate the database if we are running the app.
|
# only start the scheduler and attempt to migrate the database if we are running the app.
|
||||||
# otherwise we are running a CLI command.
|
# otherwise we are running a CLI command.
|
||||||
@ -221,6 +234,10 @@ def override_url_for():
|
|||||||
return dict(url_for=url_for_with_cache_bust)
|
return dict(url_for=url_for_with_cache_bust)
|
||||||
|
|
||||||
|
|
||||||
|
@app.context_processor
|
||||||
|
def load_config_vars():
|
||||||
|
return dict(config=app.config)
|
||||||
|
|
||||||
def url_for_with_cache_bust(endpoint, **values):
|
def url_for_with_cache_bust(endpoint, **values):
|
||||||
"""
|
"""
|
||||||
Add a query parameter based on the hash of the file, this acts as a cache bust
|
Add a query parameter based on the hash of the file, this acts as a cache bust
|
||||||
|
@ -423,7 +423,6 @@ def account_balance():
|
|||||||
has_vms=len(vms_billed)>0,
|
has_vms=len(vms_billed)>0,
|
||||||
vms_billed=vms_billed,
|
vms_billed=vms_billed,
|
||||||
warning_text=warning_text,
|
warning_text=warning_text,
|
||||||
btcpay_enabled=current_app.config["BTCPAY_ENABLED"],
|
|
||||||
payments=list(map(
|
payments=list(map(
|
||||||
lambda x: dict(
|
lambda x: dict(
|
||||||
dollars=x["dollars"],
|
dollars=x["dollars"],
|
||||||
|
@ -17,10 +17,6 @@ from capsulflask.http_client import HTTPResult
|
|||||||
from capsulflask.shared import VirtualizationInterface, VirtualMachine, OnlineHost, validate_capsul_id, my_exec_info_message
|
from capsulflask.shared import VirtualizationInterface, VirtualMachine, OnlineHost, validate_capsul_id, my_exec_info_message
|
||||||
|
|
||||||
class MockHub(VirtualizationInterface):
|
class MockHub(VirtualizationInterface):
|
||||||
def __init__(self):
|
|
||||||
self.default_network = "public1"
|
|
||||||
self.default_ipv4 = "1.1.1.1"
|
|
||||||
|
|
||||||
def capacity_avaliable(self, additional_ram_bytes):
|
def capacity_avaliable(self, additional_ram_bytes):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -33,9 +29,9 @@ class MockHub(VirtualizationInterface):
|
|||||||
{"key_type":"RSA", "content":"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCvotgzgEP65JUQ8S8OoNKy1uEEPEAcFetSp7QpONe6hj4wPgyFNgVtdoWdNcU19dX3hpdse0G8OlaMUTnNVuRlbIZXuifXQ2jTtCFUA2mmJ5bF+XjGm3TXKMNGh9PN+wEPUeWd14vZL+QPUMev5LmA8cawPiU5+vVMLid93HRBj118aCJFQxLgrdP48VPfKHFRfCR6TIjg1ii3dH4acdJAvlmJ3GFB6ICT42EmBqskz2MPe0rIFxH8YohCBbAbrbWYcptHt4e48h4UdpZdYOhEdv89GrT8BF2C5cbQ5i9qVpI57bXKrj8hPZU5of48UHLSpXG8mbH0YDiOQOfKX/Mt", "sha256":"ghee6KzRnBJhND2kEUZSaouk7CD6o6z2aAc8GPkV+GQ"},
|
{"key_type":"RSA", "content":"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCvotgzgEP65JUQ8S8OoNKy1uEEPEAcFetSp7QpONe6hj4wPgyFNgVtdoWdNcU19dX3hpdse0G8OlaMUTnNVuRlbIZXuifXQ2jTtCFUA2mmJ5bF+XjGm3TXKMNGh9PN+wEPUeWd14vZL+QPUMev5LmA8cawPiU5+vVMLid93HRBj118aCJFQxLgrdP48VPfKHFRfCR6TIjg1ii3dH4acdJAvlmJ3GFB6ICT42EmBqskz2MPe0rIFxH8YohCBbAbrbWYcptHt4e48h4UdpZdYOhEdv89GrT8BF2C5cbQ5i9qVpI57bXKrj8hPZU5of48UHLSpXG8mbH0YDiOQOfKX/Mt", "sha256":"ghee6KzRnBJhND2kEUZSaouk7CD6o6z2aAc8GPkV+GQ"},
|
||||||
{"key_type":"ECDSA", "content":"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLLgOoATz9R4aS2kk7vWoxX+lshK63t9+5BIHdzZeFE1o+shlcf0Wji8cN/L1+m3bi0uSETZDOAWMP3rHLJj9Hk=", "sha256":"aCYG1aD8cv/TjzJL0bi9jdabMGksdkfa7R8dCGm1yYs"}
|
{"key_type":"ECDSA", "content":"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLLgOoATz9R4aS2kk7vWoxX+lshK63t9+5BIHdzZeFE1o+shlcf0Wji8cN/L1+m3bi0uSETZDOAWMP3rHLJj9Hk=", "sha256":"aCYG1aD8cv/TjzJL0bi9jdabMGksdkfa7R8dCGm1yYs"}
|
||||||
]""")
|
]""")
|
||||||
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], ipv4=self.default_ipv4, ssh_host_keys=ssh_host_keys)
|
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], ipv4="1.1.1.1", ssh_host_keys=ssh_host_keys)
|
||||||
|
|
||||||
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], ipv4=self.default_ipv4)
|
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], ipv4="1.1.1.1")
|
||||||
|
|
||||||
def list_ids(self) -> list:
|
def list_ids(self) -> list:
|
||||||
return get_model().all_non_deleted_vm_ids()
|
return get_model().all_non_deleted_vm_ids()
|
||||||
@ -44,16 +40,6 @@ class MockHub(VirtualizationInterface):
|
|||||||
validate_capsul_id(id)
|
validate_capsul_id(id)
|
||||||
current_app.logger.info(f"mock create: {id} for {email}")
|
current_app.logger.info(f"mock create: {id} for {email}")
|
||||||
sleep(1)
|
sleep(1)
|
||||||
get_model().create_vm(
|
|
||||||
email=email,
|
|
||||||
id=id,
|
|
||||||
size=size,
|
|
||||||
os=os,
|
|
||||||
host=current_app.config["SPOKE_HOST_ID"],
|
|
||||||
network_name=self.default_network,
|
|
||||||
public_ipv4=self.default_ipv4,
|
|
||||||
ssh_authorized_keys=list(map(lambda x: x["name"], ssh_authorized_keys)),
|
|
||||||
)
|
|
||||||
|
|
||||||
def destroy(self, email: str, id: str):
|
def destroy(self, email: str, id: str):
|
||||||
current_app.logger.info(f"mock destroy: {id} for {email}")
|
current_app.logger.info(f"mock destroy: {id} for {email}")
|
||||||
@ -63,6 +49,7 @@ class MockHub(VirtualizationInterface):
|
|||||||
|
|
||||||
|
|
||||||
class CapsulFlaskHub(VirtualizationInterface):
|
class CapsulFlaskHub(VirtualizationInterface):
|
||||||
|
|
||||||
def synchronous_operation(self, hosts: List[OnlineHost], email: str, payload: str) -> List[HTTPResult]:
|
def synchronous_operation(self, hosts: List[OnlineHost], email: str, payload: str) -> List[HTTPResult]:
|
||||||
return self.generic_operation(hosts, email, payload, True)[1]
|
return self.generic_operation(hosts, email, payload, True)[1]
|
||||||
|
|
||||||
@ -228,11 +215,12 @@ class CapsulFlaskHub(VirtualizationInterface):
|
|||||||
# no need to do anything here since if it cant be parsed then generic_operation will handle it.
|
# no need to do anything here since if it cant be parsed then generic_operation will handle it.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
if error_message != "":
|
||||||
|
raise ValueError(f"create capsul operation {operation_id} on {assigned_hosts} failed with {error_message}")
|
||||||
|
|
||||||
if number_of_assigned != 1:
|
if number_of_assigned != 1:
|
||||||
assigned_hosts_string = ", ".join(assigned_hosts)
|
assigned_hosts_string = ", ".join(assigned_hosts)
|
||||||
raise ValueError(f"expected create capsul operation {operation_id} to be assigned to one host, it was assigned to {number_of_assigned} ({assigned_hosts_string})")
|
raise ValueError(f"expected create capsul operation {operation_id} to be assigned to one host, it was assigned to {number_of_assigned} ({assigned_hosts_string})")
|
||||||
if error_message != "":
|
|
||||||
raise ValueError(f"create capsul operation {operation_id} on {assigned_hosts_string} failed with {error_message}")
|
|
||||||
|
|
||||||
|
|
||||||
def destroy(self, email: str, id: str):
|
def destroy(self, email: str, id: str):
|
||||||
@ -275,3 +263,4 @@ class CapsulFlaskHub(VirtualizationInterface):
|
|||||||
|
|
||||||
if not result_status == "success":
|
if not result_status == "success":
|
||||||
raise ValueError(f"""failed to {command} vm "{id}" on host "{host.id}" for {email}: {result_json_string}""")
|
raise ValueError(f"""failed to {command} vm "{id}" on host "{host.id}" for {email}: {result_json_string}""")
|
||||||
|
|
||||||
|
@ -12,11 +12,9 @@ def index():
|
|||||||
|
|
||||||
@bp.route("/pricing")
|
@bp.route("/pricing")
|
||||||
def pricing():
|
def pricing():
|
||||||
vm_sizes = get_model().vm_sizes_dict()
|
|
||||||
operating_systems = get_model().operating_systems_dict()
|
operating_systems = get_model().operating_systems_dict()
|
||||||
return render_template(
|
return render_template(
|
||||||
"pricing.html",
|
"pricing.html",
|
||||||
vm_sizes=vm_sizes,
|
|
||||||
operating_systems=operating_systems
|
operating_systems=operating_systems
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ def validate_dollars():
|
|||||||
def btcpay_payment():
|
def btcpay_payment():
|
||||||
errors = list()
|
errors = list()
|
||||||
|
|
||||||
if not current_app.config['BTCPAY_ENABLED']:
|
if current_app.config['BTCPAY_PRIVATE_KEY'] == "":
|
||||||
flash("BTCPay is not enabled on this server")
|
flash("BTCPay is not enabled on this server")
|
||||||
return redirect(url_for("console.account_balance"))
|
return redirect(url_for("console.account_balance"))
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
# check available RAM and IPv4s
|
# check available RAM and IPv4s
|
||||||
|
|
||||||
ram_bytes_to_allocate="$1"
|
ram_bytes_to_allocate="$1"
|
||||||
ram_bytes_available=$(grep -E "^(size|memory_available_bytes)" /proc/spl/kstat/zfs/arcstats | awk '{sum+=$3} END {printf "%.0f", sum}')
|
ram_bytes_available="$(($(grep Available /proc/meminfo | grep -o '[0-9]*') * 1024))"
|
||||||
ram_bytes_remainder="$((ram_bytes_available - ram_bytes_to_allocate))"
|
ram_bytes_remainder="$((ram_bytes_available - ram_bytes_to_allocate))"
|
||||||
|
|
||||||
if echo "$ram_bytes_to_allocate" | grep -vqE "^[0-9]+$"; then
|
if echo "$ram_bytes_to_allocate" | grep -vqE "^[0-9]+$"; then
|
||||||
@ -11,8 +11,8 @@ if echo "$ram_bytes_to_allocate" | grep -vqE "^[0-9]+$"; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 20GB
|
# 0.25GB
|
||||||
if [ "$ram_bytes_remainder" -le $((20 * 1024 * 1024 * 1024)) ]; then
|
if [ "$ram_bytes_remainder" -le $((1 * 1024 * 1024 * 1024 / 4)) ]; then
|
||||||
echo "VM is requesting more RAM than $(hostname -f) has available."
|
echo "VM is requesting more RAM than $(hostname -f) has available."
|
||||||
echo "Bytes requested: $ram_bytes_to_allocate"
|
echo "Bytes requested: $ram_bytes_to_allocate"
|
||||||
echo "Bytes available: $ram_bytes_available"
|
echo "Bytes available: $ram_bytes_available"
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
vmname="$1"
|
vmname="$1"
|
||||||
template_file="/tank/img/$2"
|
template_file="/tank/img/$2"
|
||||||
|
qemu_tank_dir="/tank"
|
||||||
vcpus="$3"
|
vcpus="$3"
|
||||||
memory="$4"
|
memory="$4"
|
||||||
pubkeys="$5"
|
pubkeys="$5"
|
||||||
@ -50,40 +51,40 @@ if echo "$public_ipv4" | grep -vqE "^[0-9.]+$"; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
disk="/tank/vm/$vmname.qcow2"
|
disk="$vmname.qcow2"
|
||||||
cdrom="/tank/vm/$vmname.iso"
|
cdrom="$vmname.iso"
|
||||||
xml="/tank/vm/$vmname.xml"
|
xml="$vmname.xml"
|
||||||
|
|
||||||
if [ -f /tank/vm/$vmname.qcow2 ]; then
|
if [ -f /tank/vm/$vmname.qcow2 ]; then
|
||||||
echo "Randomly generated name matched an existing VM! Odds are like one in a billion. Buy a lotto ticket."
|
echo "Randomly generated name matched an existing VM! Odds are like one in a billion. Buy a lotto ticket."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cp "$template_file" "$disk"
|
cp "$template_file" "/tank/vm/$disk"
|
||||||
cp /tank/config/cyberia-cloudinit.yml /tmp/cloudinit.yml
|
cp /tank/config/cyberia-cloudinit.yml /tmp/cloudinit.yml
|
||||||
echo "$pubkeys" | while IFS= read -r line; do
|
echo "$pubkeys" | while IFS= read -r line; do
|
||||||
echo " - $line" >> /tmp/cloudinit.yml
|
echo " - $line" >> /tmp/cloudinit.yml
|
||||||
done
|
done
|
||||||
|
|
||||||
cloud-localds "$cdrom" /tmp/cloudinit.yml
|
cloud-localds "/tank/vm/$cdrom" /tmp/cloudinit.yml
|
||||||
|
|
||||||
qemu-img resize "$disk" "$root_volume_size"
|
qemu-img resize "/tank/vm/$disk" "$root_volume_size"
|
||||||
virt-install \
|
virt-install \
|
||||||
--memory "$memory" \
|
--memory "$memory" \
|
||||||
--vcpus "$vcpus" \
|
--vcpus "$vcpus" \
|
||||||
--name "$vmname" \
|
--name "$vmname" \
|
||||||
--disk "$disk",bus=virtio \
|
--disk "$qemu_tank_dir/vm/$disk",bus=virtio \
|
||||||
--disk "$cdrom",device=cdrom \
|
--disk "$qemu_tank_dir/vm/$cdrom",device=cdrom \
|
||||||
--os-type Linux \
|
--os-type Linux \
|
||||||
--os-variant generic \
|
--os-variant generic \
|
||||||
--virt-type kvm \
|
--virt-type kvm \
|
||||||
--graphics vnc,listen=127.0.0.1 \
|
--graphics vnc,listen=127.0.0.1 \
|
||||||
--network network=$network_name,filterref=clean-traffic,model=virtio \
|
--network network=$network_name,model=virtio \
|
||||||
--import \
|
--import \
|
||||||
--print-xml > "$xml"
|
--print-xml > "/tank/vm/$xml"
|
||||||
|
|
||||||
chmod 0600 "$xml" "$disk" "$cdrom"
|
chmod 0600 "/tank/vm/$xml" "/tank/vm/$disk" "/tank/vm/$cdrom"
|
||||||
virsh define "$xml"
|
virsh define "/tank/vm/$xml"
|
||||||
virsh start "$vmname"
|
virsh start "$vmname"
|
||||||
|
|
||||||
echo "success"
|
echo "success"
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 1.2 KiB |
@ -241,6 +241,7 @@ thead {
|
|||||||
background: #bdc7b812;
|
background: #bdc7b812;
|
||||||
}
|
}
|
||||||
td, th {
|
td, th {
|
||||||
|
|
||||||
padding: 0.1em 1em;
|
padding: 0.1em 1em;
|
||||||
}
|
}
|
||||||
table.small td, table.small th {
|
table.small td, table.small th {
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
<a href="/payment/stripe">Add funds with Credit/Debit (stripe)</a>
|
<a href="/payment/stripe">Add funds with Credit/Debit (stripe)</a>
|
||||||
<ul><li>notice: stripe will load nonfree javascript </li></ul>
|
<ul><li>notice: stripe will load nonfree javascript </li></ul>
|
||||||
</li>
|
</li>
|
||||||
{% if btcpay_enabled %}
|
{% if config['BTCPAY_PRIVATE_KEY'] != "" %}
|
||||||
<li><a href="/payment/btcpay">Add funds with Bitcoin/Litecoin/Monero (btcpay)</a></li>
|
<li><a href="/payment/btcpay">Add funds with Bitcoin/Litecoin/Monero (btcpay)</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
<nav>
|
<nav>
|
||||||
<div class="row justify-space-between half-margin">
|
<div class="row justify-space-between half-margin">
|
||||||
<div>
|
<div>
|
||||||
<a href="/"><b>Capsul</b></a>💊
|
🦉 <a href="/"><b>YOLOCOLO</b></a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
@ -27,7 +27,6 @@
|
|||||||
<div class="row justify-center half-margin wrap nav-links">
|
<div class="row justify-center half-margin wrap nav-links">
|
||||||
<a href="/pricing">Pricing</a>
|
<a href="/pricing">Pricing</a>
|
||||||
<a href="/faq">FAQ</a>
|
<a href="/faq">FAQ</a>
|
||||||
<a href="/changelog">Changelog</a>
|
|
||||||
|
|
||||||
{% if session["account"] %}
|
{% if session["account"] %}
|
||||||
<a href="/console">Capsuls</a>
|
<a href="/console">Capsuls</a>
|
||||||
@ -47,11 +46,12 @@
|
|||||||
</main>
|
</main>
|
||||||
{% block subcontent %}{% endblock %}
|
{% block subcontent %}{% endblock %}
|
||||||
<footer>
|
<footer>
|
||||||
(c) Attribution-ShareAlike 4.0 International <br/>
|
This server runs <a
|
||||||
A service by Cyberia Computer Club 2020-<span class="bigtext">∞</span> <br/>
|
href="https://giit.cyberia.club/~forest/capsul-flask">capsul-flask</a> by
|
||||||
<br/>
|
Cyberia Computer Club, available under the <a
|
||||||
<br/>
|
href="https://creativecommons.org/licenses/by-sa/4.0/">Attribution-ShareAlike
|
||||||
<a href="https://giit.cyberia.club/~forest/capsul-flask/tree/master/capsulflask{% block pagesource %}{% endblock %}">View page source</a>
|
4.0 International</a> licence.<br/><br/>
|
||||||
|
<a href="https://git.autonomic.zone/3wordchant/capsul-flask/src/branch/yolocolo/capsulflask{% block pagesource %}{% endblock %}">View page source</a>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -10,82 +10,32 @@
|
|||||||
<p>
|
<p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
Which instance type should I buy?
|
What is this?
|
||||||
<p>There are no hard rules for this sort of thing, but here are some guidelines:</p>
|
<p>
|
||||||
<p>f1-xs: blog, vpn, bot, cgit</p>
|
This is a <strong>technical demo</strong> of <a
|
||||||
<p>f1-s: a bot, owncloud, gitea, popular blog</p>
|
href="https://giit.cyberia.club/~forest/capsul-flask">Capsul</a>, for the
|
||||||
<p>f1-m: docker host, build system</p>
|
as-yet-untitled <a href="https://coops.tech">Cotech</a> server hosting
|
||||||
<p>f1-l: large webservice, rotund java app</p>
|
initiative, which you can <a
|
||||||
<p>f1-x: gitlab (wow such memory very devops</p>
|
href="https://community.coops.tech/t/call-for-input-v2-co-op-vps-survey/2802/9">read
|
||||||
<p>f1-xx: something gargantuan</p>
|
about on the Cotech forum</a>.
|
||||||
|
</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
How do I log in?
|
What do you mean, "technical demo"?
|
||||||
<p>ssh to the ip provided to you using the cyberian user.</p>
|
<p>No backups</p>
|
||||||
<pre class='code'>$ ssh cyberian@1.2.3.4</pre>
|
<p>No service level agreement</p>
|
||||||
<p>For more information, see <a href="/about-ssh">Understanding the Secure Shell Protocol (SSH)</a>.</p>
|
<p>"Best effort" support</p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
How do I change to the root user?
|
Where can I get this, but, more reliable?
|
||||||
<p>The cyberian user has passwordless sudo access by default. This should work:</p>
|
<p>Cyberia, the authors of this platform, run the canonical instance, <a
|
||||||
<pre class='code'>
|
href="https://capsul.org">Capsul.org</a>, on hardware they own. Please
|
||||||
# Linux
|
send them your money! (cash, crypto, or card accepted).</p>
|
||||||
$ sudo su -
|
|
||||||
|
|
||||||
# OpenBSD
|
|
||||||
$ doas su -</pre>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Do you offer reverse DNS?
|
How do I use this system?
|
||||||
<p>We do, but right now it's a manual process. Shoot us an email and we'll get it done.</p>
|
<p>Please see <a href="https://capsul.org/faq">the official Capsul FAQ
|
||||||
</li>
|
page</a>.</p>
|
||||||
<li>
|
|
||||||
What if I don't pay / don't maintain my payments?
|
|
||||||
<p>Your VM will eventually be deleted.
|
|
||||||
Capsul will send you a few inoffensive reminders as that termination date approaches.
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Besides my virtual machines and payments, what information do you keep about me?
|
|
||||||
<p>We associate an email address with every VM so that we can track payment and respond to support requests.</p>
|
|
||||||
<p>If you pay with a credit card, Stripe stores some additional details about you that we literally cannot delete.</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
What can I do with my VM?
|
|
||||||
<p>Make it into a mailserver, a tor relay, a VPN host, whatever you'd like - we do have one small request, though.</p>
|
|
||||||
<p>Crypto mining on capsul is currently considered obnoxious behavior, because the hashrates on our CPUs is so low and because mining crypto consumes entire processor cores that could have otherwise been shared between many dozens of other users.</p>
|
|
||||||
<p>In the future, if we have plentiful CPU resources, we may come out with a tier more suitable for mining - maybe a high cpu tier or similar, where each VM gets a full dedicated core and sharing them is not anticipated.</p>
|
|
||||||
<p>We will never snoop on your traffic or inspect what's going on inside of our customer virtual machines - we don't want to. We hope that you'll extend us a similar courtesy and try not to use too much of our shared CPU resources. Capsul is currently a shared (resource-wise) world, and we all must live in it together!</p>
|
|
||||||
<p>Also, mandatory: our systems exist within the USA, and as such those systems are bound by US law.</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Can you recover my passwords/insert new keys?
|
|
||||||
<p>Can we? Technically yes. Will we? No, never. It would violate the trust that our users have in us.
|
|
||||||
We have no interest in touching client VMs after they're running.
|
|
||||||
We promise to keep your machines running smoothly.
|
|
||||||
If you lose access to your VM, that's on you.</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Do you offer refunds?
|
|
||||||
<p>Not now, but email us and we can probably figure something out.</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Where do the VMs run? Is it on a machine that you guys own/control?
|
|
||||||
<p>Capsul runs on a server named Baikal which Cyberia built from scratch & mailed to a datacenter
|
|
||||||
in Georgia called CyberWurx. CyberWurx staff installed it for us in a rack space that
|
|
||||||
Cyberia pays for. </p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Do you offer support?
|
|
||||||
<p>Yep, see <a href="/support">our support page</a>.</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Do you have an SLA?
|
|
||||||
<p>No, but we normally respond pretty quickly.</p>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Will you implement feature X?
|
|
||||||
<p>Maybe! Email <a href="mailto:ops@cyberia.club">ops@cyberia.club</a> and ask us about it.</p>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</p>
|
</p>
|
||||||
|
@ -1,31 +1,26 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>CAPSUL</h1>
|
<h1>
|
||||||
<pre>
|
<pre>
|
||||||
.-.
|
_ _
|
||||||
/:::\
|
_ _ ___ | | ___ ___ ___ | | ___
|
||||||
/::::/
|
| | | |/ _ \| |/ _ \ / __/ _ \| |/ _ \
|
||||||
/ `-:/
|
| |_| | (_) | | (_) | (_| (_) | | (_) |
|
||||||
/ /
|
\__, |\___/|_|\___/ \___\___/|_|\___/
|
||||||
\ /
|
|___/
|
||||||
`"`
|
|
||||||
</pre>
|
</pre>
|
||||||
<span>Simple, fast, private compute by <a href="https://cyberia.club">cyberia.club</a></span>
|
<span>Co-operative hosting using <a href="https://cyberia.club">Cyberia</a>'s Capsul</span>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block subcontent %}
|
{% block subcontent %}
|
||||||
<p>
|
<p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Low friction: simply log in with your email address and fund your account with Credit/Debit or Cryptocurrency</li>
|
<li>Sign up for an account!</li>
|
||||||
<li>All root disks are backed up at no charge</li>
|
<li>Add some funds!</li>
|
||||||
<li>All storage is fast, local, and solid-state</li>
|
<li>Create a VPS!</li>
|
||||||
<li>All network connections are low latency</li>
|
<li>Give your feedback!</li>
|
||||||
<li>Supported by amazing volunteers from Cyberia</li>
|
|
||||||
<li>Upfront prices, no confusing billing</li>
|
|
||||||
<li>Operated by a Minnesota non-profit organization that will never exploit you</li>
|
|
||||||
<li>We donate a portion of our proceeds to likeminded hacker groups around the globe</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</p>
|
</p>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -7,42 +7,17 @@
|
|||||||
<h1>CAPSUL TYPES & PRICING</h1>
|
<h1>CAPSUL TYPES & PRICING</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="row half-margin">
|
<div class="row half-margin">
|
||||||
<table>
|
<p>
|
||||||
<thead>
|
Rates for this service isn't set yet. You can see Cyberia's Capsul pricing
|
||||||
<tr>
|
on <a href="https://capsul.org/pricing">their website</a>.
|
||||||
<th>type</th>
|
</p>
|
||||||
<th>monthly*</th>
|
|
||||||
<th>cpus</th>
|
|
||||||
<th>mem</th>
|
|
||||||
<th>ssd</th>
|
|
||||||
<th>net</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for vm_size_key, vm_size in vm_sizes.items() %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ vm_size_key }}</td>
|
|
||||||
<td>${{ vm_size['dollars_per_month'] }}</td>
|
|
||||||
<td>{{ vm_size['vcpus'] }}</td>
|
|
||||||
<td>{{ vm_size['memory_mb'] }}</td>
|
|
||||||
<td>25G</td>
|
|
||||||
<td>{{ vm_size['bandwidth_gb_per_month'] }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row half-margin">
|
<div>
|
||||||
<pre>
|
<pre>
|
||||||
* net is calculated as a per-month average
|
SUPPORTED OPERATING SYSTEMS:
|
||||||
* vms are billed for a minimum of 24 hours upon creation
|
|
||||||
* all VMs come standard with one public IPv4 address
|
|
||||||
|
|
||||||
SUPPORTED OPERATING SYSTEMS:
|
{% for os_id, os in operating_systems.items() %} - {{ os.description }}
|
||||||
|
{% endfor %}
|
||||||
|
</pre>
|
||||||
{% for os_id, os in operating_systems.items() %} - {{ os.description }}
|
|
||||||
{% endfor %}
|
|
||||||
</pre>
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -7,20 +7,14 @@
|
|||||||
<h1>SUPPORT</h1>
|
<h1>SUPPORT</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="row half-margin">
|
<div class="row half-margin">
|
||||||
<a href="mailto:support@cyberia.club?subject=Please%20help!">support@cyberia.club</a>
|
<a href="mailto:yolocolo@doesthisthing.work?subject=Please%20help!">yolocolo@doesthisthing.work</a>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block subcontent %}
|
{% block subcontent %}
|
||||||
<p>
|
<p>
|
||||||
Note: We maintain a searchable archive of all support emails at
|
You can also find us on Matrix: <a
|
||||||
<a href="https://lists.cyberia.club/~cyberia/support">https://lists.cyberia.club/~cyberia/support</a>
|
href="https://matrix.to/#/#untitled-hosting.public:autonomic.zone">#untitled-hosting.public:autonomic.zone</a>.
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
If you do not want your mail to appear in a public archive, email <a href="mailto:capsul@cyberia.club?subject=Please%20help!">capsul@cyberia.club</a> instead.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Please describe your problem or feature request, and we will do our best to get back to you promptly. Thank you very much.
|
|
||||||
</p>
|
</p>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
38
docker-compose.yml
Normal file
38
docker-compose.yml
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: 3wordchant/capsul-flask:latest
|
||||||
|
build: .
|
||||||
|
volumes:
|
||||||
|
- "./:/app/code"
|
||||||
|
- "../tank:/tank"
|
||||||
|
- "/var/run/libvirt/libvirt-sock:/var/run/libvirt/libvirt-sock"
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
|
ports:
|
||||||
|
- "5000:5000"
|
||||||
|
environment:
|
||||||
|
- "POSTGRES_CONNECTION_PARAMETERS=host=db port=5432 user=capsul password=capsul dbname=capsul"
|
||||||
|
- SPOKE_MODEL=shell-scripts
|
||||||
|
#- FLASK_DEBUG=1
|
||||||
|
- BASE_URL=http://localhost:5000
|
||||||
|
- ADMIN_PANEL_ALLOW_EMAIL_ADDRESSES=3wc.capsul@doesthisthing.work
|
||||||
|
- VIRSH_DEFAULT_CONNECT_URI=qemu:///system
|
||||||
|
# The image uses gunicorn by default, let's override it with Flask's
|
||||||
|
# built-in development server
|
||||||
|
command: ["flask", "run", "-h", "0.0.0.0", "-p", "5000"]
|
||||||
|
devices:
|
||||||
|
- "/dev/kvm:/dev/kvm"
|
||||||
|
db:
|
||||||
|
image: "postgres:9.6.5-alpine"
|
||||||
|
volumes:
|
||||||
|
- "postgres:/var/lib/postgresql/data"
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: capsul
|
||||||
|
POSTGRES_PASSWORD: capsul
|
||||||
|
POSTGRES_DB: capsul
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres:
|
Reference in New Issue
Block a user