Skip to content

Commit 7736341

Browse files
authored
Merge pull request #79 from threefoldtech/flist-local-sync
readonly: support readonly mode
2 parents 33d326f + 986b4c2 commit 7736341

File tree

5 files changed

+147
-2
lines changed

5 files changed

+147
-2
lines changed

src/config.py.sample

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,17 @@ config = {
8585
##
8686
# 'userdata-root-path': '/opt/0-hub/public',
8787
# 'workdir-root-path': '/opt/0-hub/workdir',
88-
88+
89+
## In order to host a hub which is a mirror of another
90+
## hub, since hub right now don't support multiple master,
91+
## you should enable this read-only mode to avoid modifying
92+
## your local database and be able to stay synced with your
93+
## main hub instance. For a normal or main hub instance,
94+
## keep this setting to False
95+
'readonly': False,
96+
8997
## By default, the hub is made to be used publicly
90-
## and needs to be protected (with itsyou.online)
98+
## and needs to be protected (with threefold connect)
9199
##
92100
## If you are running a local test hub on your local
93101
## network and you don't have any security issue, you

src/flist-uploader.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,13 @@ def logout():
272272
def login_method():
273273
return internalRedirect("logins.html")
274274

275+
@app.route('/readonly')
276+
def read_only_mode():
277+
if not config['readonly']:
278+
return redirect("/")
279+
280+
return globalTemplate("readonly.html", {})
281+
275282
@app.route('/login-iyo')
276283
@hub.itsyouonline.force_login()
277284
def login_iyo():

src/hub/security.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ def apicall():
88
def decorator(handler):
99
@wraps(handler)
1010
def _wrapper(*args, **kwargs):
11+
if config['readonly']:
12+
return jsonify({"message": "readonly mode", "status": "error"}), 400
13+
1114
if not config['authentication']:
1215
session['accounts'] = ['Administrator']
1316
session['username'] = 'Administrator'
@@ -48,6 +51,9 @@ def protected():
4851
def decorator(handler):
4952
@wraps(handler)
5053
def _wrapper(*args, **kwargs):
54+
if config['readonly']:
55+
return redirect("/readonly")
56+
5157
if not config['authentication']:
5258
session['accounts'] = ['Administrator']
5359
session['username'] = 'Administrator'

src/templates/readonly.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{% extends "layout.html" %}
2+
{% block title %}Zero-OS Hub{% endblock %}
3+
4+
{% block content %}
5+
<div class="jumbotron">
6+
<div class="container">
7+
<h1>Instance modification disabled</h1>
8+
<p>This Hub instance doesn't allow modification, this is a <code>read-only hub instance</code>.</p>
9+
<p>This is probably a mirror of a main instance, you should point to that one, or
10+
this instance is a special serve-only instance.<p>
11+
</div>
12+
</div>
13+
{% endblock %}

tools/flist-copy-local.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import redis
2+
import tempfile
3+
import requests
4+
import subprocess
5+
import json
6+
import os
7+
import sys
8+
9+
class FListRemoteClone:
10+
def __init__(self, host, port, basehub="https://hub.grid.tf"):
11+
self.host = host
12+
self.port = port
13+
self.basehub = basehub
14+
15+
self.local = redis.Redis(host, port)
16+
self.remote = redis.Redis("hub.grid.tf", 9900)
17+
18+
self.zflist = "/home/maxux/git/0-flist/zflist/zflist"
19+
self.backend = ""
20+
self.workdir = tempfile.mkdtemp(prefix="zflist-cloning")
21+
self.tempdir = tempfile.mkdtemp(prefix="zflist-source")
22+
23+
print(self.workdir)
24+
print(self.tempdir)
25+
26+
self.environ = dict(
27+
os.environ,
28+
ZFLIST_JSON="1",
29+
ZFLIST_MNT=self.workdir
30+
)
31+
32+
def authenticate(self):
33+
pass
34+
35+
def download(self, target):
36+
url = f"{self.basehub}/{target}"
37+
destination = f"{self.tempdir}/download.flist"
38+
39+
print(f"[+] fetching: {url}")
40+
41+
r = requests.get(url)
42+
with open(destination, "wb") as f:
43+
f.write(r.content)
44+
45+
length = len(r.content) / 1024
46+
print(f"[+] fetched {length:.2f} KB into {destination}")
47+
48+
return destination
49+
50+
def execute(self, args):
51+
command = [self.zflist] + args
52+
53+
print(command)
54+
p = subprocess.Popen(command, env=self.environ, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
55+
(output, err) = p.communicate()
56+
57+
return json.loads(output)
58+
59+
def chunks(self, flist):
60+
self.execute(["open", flist])
61+
reply = self.execute(["chunks"])
62+
63+
chunks = reply["response"]["content"]
64+
bchunks = []
65+
66+
for chunk in chunks:
67+
bchunk = bytes.fromhex(chunk)
68+
bchunks.append(bchunk)
69+
70+
return bchunks
71+
72+
def metadata(self):
73+
pass
74+
75+
def commit(self, destination):
76+
self.execute(["commit", destination])
77+
78+
def sync(self, chunks):
79+
proceed = 0
80+
total = len(chunks)
81+
82+
print("[+] syncing database...")
83+
84+
for chunk in chunks:
85+
data = self.remote.get(chunk)
86+
self.local.execute_command("SETX", chunk, data)
87+
88+
proceed += 1
89+
percent = (proceed / total) * 100
90+
91+
sys.stdout.write(f"\r[+] syncing database: {proceed} / {total} [{percent:.2f} %%]")
92+
sys.stdout.flush()
93+
94+
print("[+] database synchronized")
95+
96+
def clone(self, target):
97+
flist = self.download(target)
98+
chunks = self.chunks(flist)
99+
self.sync(chunks)
100+
self.metadata()
101+
self.commit("/tmp/destination.flist")
102+
103+
104+
if len(sys.argv) < 2:
105+
print("[-] missing target flist to clone")
106+
sys.exit(1)
107+
108+
target = sys.argv[1]
109+
110+
x = FListRemoteClone("127.0.0.1", 9900)
111+
x.clone(target)

0 commit comments

Comments
 (0)