Skip to content

Commit 7a5efb4

Browse files
Merge pull request #141 from SelfhostedPro/develop
Alpha 3 (v0.0.3-alpha) Update
2 parents d11eea3 + 5e3c11e commit 7a5efb4

File tree

23 files changed

+1116
-107
lines changed

23 files changed

+1116
-107
lines changed

backend/api/actions/apps.py

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
from ..utils import *
66

77
from datetime import datetime
8+
import time
9+
import subprocess
810
import docker
911

1012

@@ -21,12 +23,22 @@ def get_running_apps():
2123

2224
return apps_list
2325

26+
def check_app_updates():
27+
apps_list = []
28+
dclient = docker.from_env()
29+
apps = dclient.containers.list(all=True)
30+
for app in apps:
31+
if check_updates(app.image.tags[0]):
32+
apps_list.append(app.name)
33+
return apps_list
34+
2435
def get_apps():
2536
apps_list = []
2637
dclient = docker.from_env()
2738
apps = dclient.containers.list(all=True)
2839
for app in apps:
2940
attrs = app.attrs
41+
3042
attrs.update(conv2dict('name', app.name))
3143
attrs.update(conv2dict('ports', app.ports))
3244
attrs.update(conv2dict('short_id', app.short_id))
@@ -39,7 +51,7 @@ def get_app(app_name):
3951
dclient = docker.from_env()
4052
app = dclient.containers.get(app_name)
4153
attrs = app.attrs
42-
54+
4355
attrs.update(conv2dict('ports', app.ports))
4456
attrs.update(conv2dict('short_id', app.short_id))
4557
attrs.update(conv2dict('name', app.name))
@@ -148,6 +160,63 @@ def app_action(app_name, action):
148160
apps_list = get_apps()
149161
return apps_list
150162

163+
def app_update(app_name):
164+
dclient = docker.from_env()
165+
try:
166+
old = dclient.containers.get(app_name)
167+
except Exception as exc:
168+
print(exc)
169+
if exc.response.status_code == 404:
170+
raise HTTPException(status_code=exc.response.status_code, detail="Unable to get container ID")
171+
else:
172+
raise HTTPException(status_code=exc.response.status_code, detail=exc.explanation)
173+
174+
volumes ={'/var/run/docker.sock': {'bind':'/var/run/docker.sock', 'mode': 'rw'}}
175+
try:
176+
updater = dclient.containers.run(
177+
image='containrrr/watchtower:latest',
178+
command='--run-once '+old.name,
179+
remove=True,
180+
detach=True,
181+
volumes=volumes
182+
)
183+
except Exception as exc:
184+
print(exc)
185+
raise HTTPException(status_code=exc.response.status_code, detail=exc.explanation)
186+
187+
print('**** Updating '+old.name+'****')
188+
result = updater.wait(timeout=120)
189+
print(result)
190+
time.sleep(1)
191+
return get_apps()
192+
193+
def update_self():
194+
dclient = docker.from_env()
195+
bash_command = "head -1 /proc/self/cgroup|cut -d/ -f3"
196+
yacht_id = subprocess.check_output(['bash','-c', bash_command]).decode('UTF-8').strip()
197+
try:
198+
yacht = dclient.containers.get(yacht_id)
199+
except Exception as exc:
200+
print(exc)
201+
if exc.response.status_code == 404:
202+
raise HTTPException(status_code=exc.response.status_code, detail="Unable to get Yacht container ID")
203+
else:
204+
raise HTTPException(status_code=exc.response.status_code, detail=exc.explanation)
205+
206+
volumes ={'/var/run/docker.sock': {'bind':'/var/run/docker.sock', 'mode': 'rw'}}
207+
print('**** Updating '+yacht.name+'****')
208+
updater = dclient.containers.run(
209+
image='containrrr/watchtower:latest',
210+
command='--run-once '+yacht.name,
211+
remove=True,
212+
detach=True,
213+
volumes=volumes
214+
)
215+
result = updater.wait(timeout=120)
216+
print(result)
217+
time.sleep(1)
218+
return result
219+
151220
def prune_images():
152221
dclient = docker.from_env()
153222
deleted_everything = {}
@@ -159,4 +228,25 @@ def prune_images():
159228
deleted_everything.update(deleted_volumes)
160229
deleted_everything.update(deleted_images)
161230

162-
return deleted_everything
231+
return deleted_everything
232+
def prune_resources(resource):
233+
dclient = docker.from_env()
234+
action = getattr(dclient, resource)
235+
deleted_resource = action.prune()
236+
return deleted_resource
237+
238+
239+
def check_self_update():
240+
dclient = docker.from_env()
241+
bash_command = "head -1 /proc/self/cgroup|cut -d/ -f3"
242+
yacht_id = subprocess.check_output(['bash','-c', bash_command]).decode('UTF-8').strip()
243+
try:
244+
yacht = dclient.containers.get(yacht_id)
245+
except Exception as exc:
246+
print(exc)
247+
if exc.response.status_code == 404:
248+
raise HTTPException(status_code=exc.response.status_code, detail="Unable to get Yacht container ID")
249+
else:
250+
raise HTTPException(status_code=exc.response.status_code, detail=exc.explanation)
251+
252+
return check_updates(yacht.image.tags[0])

backend/api/db/schemas/templates.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ class TemplateRead(TemplateBase):
4242
updated_at: datetime
4343
created_at: datetime
4444

45+
class TemplateReadAll(TemplateBase):
46+
items: List[TemplateItem] = []
4547

4648
class TemplateItems(TemplateRead):
4749
items: List[TemplateItem] = []

backend/api/routers/app_settings.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ def export_settings(db: Session = Depends(get_db)):
3737
def import_settings(db: Session = Depends(get_db), upload: UploadFile = File(...)):
3838
return crud.import_settings(db=db, upload=upload)
3939

40-
@router.get("/prune", dependencies=[Depends(get_active_user)])
41-
def prune_images():
42-
return apps.prune_images()
40+
@router.get("/prune/{resource}", dependencies=[Depends(get_active_user)])
41+
def prune_resources(resource: str):
42+
return apps.prune_resources(resource)
43+
44+
@router.get('/update', dependencies=[Depends(get_active_user)])
45+
def update_self():
46+
return apps.update_self()
47+
48+
@router.get('/check/update', dependencies=[Depends(get_active_user)])
49+
def check_self_update():
50+
return apps.check_self_update()

backend/api/routers/apps.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ def get_db():
3636
def index():
3737
return actions.get_apps()
3838

39+
@router.get('/updates', dependencies=[Depends(get_active_user)])
40+
def check_updates():
41+
return actions.check_app_updates()
3942

4043
@router.get("/{app_name}", dependencies=[Depends(get_active_user)])
4144
def get_container_details(app_name):
@@ -51,6 +54,9 @@ def get_container_processes(app_name):
5154
def get_container_logs(app_name):
5255
return actions.get_app_logs(app_name=app_name)
5356

57+
@router.get("/{app_name}/update", dependencies=[Depends(get_active_user)])
58+
def update_container(app_name):
59+
return actions.app_update(app_name)
5460

5561
@router.get("/{app_name}/{action}", dependencies=[Depends(get_active_user)])
5662
def container_actions(app_name, action):
@@ -116,7 +122,6 @@ async def stats(websocket: WebSocket, app_name: str):
116122
cpu_percent = await calculate_cpu_percent(line)
117123

118124
full_stats = {
119-
"time": line['read'],
120125
"cpu_percent": cpu_percent,
121126
"mem_current": mem_current,
122127
"mem_total": mem_total,
@@ -174,7 +179,6 @@ async def process_container(name, stats, websocket):
174179

175180
full_stats = {
176181
"name": name,
177-
"time": line['read'],
178182
"cpu_percent": cpu_percent,
179183
"mem_current": mem_current,
180184
"mem_total": mem_total,

backend/api/utils.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from .auth import user_db
1111
from .settings import Settings
1212
import aiodocker
13+
import docker
1314
import json
1415
settings = Settings()
1516

@@ -49,7 +50,7 @@ def conv_ports2dict(data: List[str]) -> List[Dict[str, str]]:
4950
for port_data in data:
5051
for label, port in port_data.items():
5152
if not re.match(REGEXP_PORT_ASSIGN, port, flags=re.IGNORECASE):
52-
raise ValueError('Malformed port assignment.')
53+
raise HTTPException(status_code=500, detail='Malformed port assignment.'+port_data)
5354

5455
hport, cport = None, port
5556
if delim in cport:
@@ -65,7 +66,7 @@ def conv_ports2dict(data: List[str]) -> List[Dict[str, str]]:
6566
portlst = []
6667
for port_data in data:
6768
if not re.match(REGEXP_PORT_ASSIGN, port_data, flags=re.IGNORECASE):
68-
raise ValueError('Malformed port assignment.')
69+
raise HTTPException(status_code=500, detail='Malformed port assignment.'+port_data)
6970

7071
hport, cport = None, port_data
7172
if delim in cport:
@@ -182,6 +183,14 @@ def conv_volumes2data(data):
182183
def conv_env2data(data):
183184
# Set is depracated. Name is the actual value. Label is the name of the field.
184185
# Label is the label of the label field.
186+
db = SessionLocal()
187+
t_variables = db.query(models.TemplateVariables).all()
188+
189+
for i,variable in enumerate(data):
190+
for t_var in t_variables:
191+
if t_var.variable in variable.default:
192+
new_var = data[i].default.replace(t_var.variable, t_var.replacement)
193+
variable.default = new_var
185194
delim = '='
186195
return [delim.join((d.name, d.default)) for d in data]
187196

@@ -337,3 +346,26 @@ async def get_app_stats(app_name):
337346
"mem_percent": (mem_current / mem_total) * 100.0,
338347
}
339348
yield json.dumps(full_stats)
349+
350+
def get_update_ports(ports):
351+
if ports:
352+
portdir={}
353+
for hport in ports:
354+
for d in ports[hport]:
355+
portdir.update({str(hport): d.get('HostPort') })
356+
return portdir
357+
else:
358+
return None
359+
360+
def check_updates(tag):
361+
if tag:
362+
dclient = docker.from_env()
363+
current = dclient.images.get(tag)
364+
new = dclient.images.get_registry_data(tag)
365+
if new.attrs['Descriptor']['digest'] in current.attrs['RepoDigests'][0]:
366+
return False
367+
else:
368+
return True
369+
370+
else:
371+
return False

frontend/package-lock.json

Lines changed: 19 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@
1414
"chart.js": "^2.9.3",
1515
"chartjs-plugin-crosshair": "^1.1.6",
1616
"core-js": "^3.6.5",
17-
"moment": "^2.28.0",
18-
"vee-validate": "^3.3.11",
17+
"moment": "^2.29.0",
18+
"vee-validate": "^3.4.0",
1919
"vue": "^2.6.12",
2020
"vue-chartjs": "^3.5.1",
21-
"vue-router": "^3.4.3",
22-
"vuetify": "^2.3.10",
21+
"vue-router": "^3.4.5",
22+
"vuetify": "^2.3.12",
2323
"vuex": "^3.4.0"
2424
},
2525
"devDependencies": {

0 commit comments

Comments
 (0)