Skip to content

Commit 71590f9

Browse files
authored
Merge pull request #4 from pvizeli/selfupdate
Allow supervisor to update itself.
2 parents 78d1e1d + e1028d6 commit 71590f9

File tree

12 files changed

+97
-39
lines changed

12 files changed

+97
-39
lines changed

API.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ Communicate over unix socket with a host daemon.
1111
# reboot
1212
# shutdown
1313
# host-update [v]
14-
# supervisor-update [v]
1514
1615
# network info
1716
# network hostname xy
@@ -24,9 +23,8 @@ Communicate over unix socket with a host daemon.
2423

2524
level:
2625
- 1: power functions
27-
- 2: supervisor update
28-
- 4: host update
29-
- 8: network functions
26+
- 2: host update
27+
- 4: network functions
3028

3129
Answer:
3230
```

hassio/__main__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import asyncio
33
import logging
44
import signal
5+
import sys
56

67
import hassio.bootstrap as bootstrap
78
import hassio.core as core
@@ -33,4 +34,6 @@
3334

3435
loop.run_forever()
3536
loop.close()
37+
3638
_LOGGER.info("Close Hassio")
39+
sys.exit(hassio.exit_code)

hassio/api/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ def register_network(self, host_controll):
4141
self.webapp.router.add_get('/network/info', api_net.info)
4242
self.webapp.router.add_get('/network/options', api_net.options)
4343

44-
def register_supervisor(self, host_controll, addons):
44+
def register_supervisor(self, supervisor, addons):
4545
"""Register supervisor function."""
4646
api_supervisor = APISupervisor(
47-
self.config, self.loop, host_controll, addons)
47+
self.config, self.loop, supervisor, addons)
4848

4949
self.webapp.router.add_get('/supervisor/ping', api_supervisor.ping)
5050
self.webapp.router.add_get('/supervisor/info', api_supervisor.info)

hassio/api/supervisor.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
"""Init file for HassIO supervisor rest api."""
2+
import asyncio
23
import logging
34

45
import voluptuous as vol
56

6-
from .util import api_process, api_process_hostcontroll, api_validate
7+
from .util import api_process, api_validate
78
from ..const import (
89
ATTR_ADDONS, ATTR_VERSION, ATTR_CURRENT, ATTR_BETA, HASSIO_VERSION)
910

@@ -22,11 +23,11 @@
2223
class APISupervisor(object):
2324
"""Handle rest api for supervisor functions."""
2425

25-
def __init__(self, config, loop, host_controll, addons):
26+
def __init__(self, config, loop, supervisor, addons):
2627
"""Initialize supervisor rest api part."""
2728
self.config = config
2829
self.loop = loop
29-
self.host_controll = host_controll
30+
self.supervisor = supervisor
3031
self.addons = addons
3132

3233
@api_process
@@ -55,13 +56,13 @@ async def options(self, request):
5556

5657
return self.config.save()
5758

58-
@api_process_hostcontroll
59+
@api_process
5960
async def update(self, request):
60-
"""Update host OS."""
61+
"""Update supervisor OS."""
6162
body = await api_validate(SCHEMA_VERSION, request)
6263
version = body.get(ATTR_VERSION, self.config.current_hassio)
6364

64-
if version == HASSIO_VERSION:
65+
if version == self.supervisor.version:
6566
raise RuntimeError("Version is already in use")
6667

67-
return await self.host_controll.supervisor_update(version=version)
68+
return await asyncio.shield(self.supervisor.update(version))

hassio/config.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
HASSIO_SSL = "{}/ssl"
1616
HASSIO_CURRENT = 'hassio_current'
17+
HASSIO_CLEANUP = 'hassio_cleanup'
1718

1819
ADDONS_REPO = "{}/addons"
1920
ADDONS_DATA = "{}/addons_data"
@@ -87,6 +88,20 @@ def upstream_beta(self, value):
8788
"""Set beta upstream mode."""
8889
self._data[UPSTREAM_BETA] = bool(value)
8990

91+
@property
92+
def hassio_cleanup(self):
93+
"""Return Version they need to cleanup."""
94+
return self._data.get(HASSIO_CLEANUP)
95+
96+
@hassio_cleanup.setter
97+
def hassio_cleanup(self, version):
98+
"""Set or remove cleanup flag."""
99+
if version is None:
100+
self._data.pop(HASSIO_CLEANUP, None)
101+
else:
102+
self._data[HASSIO_CLEANUP] = version
103+
self.save()
104+
90105
@property
91106
def homeassistant_image(self):
92107
"""Return docker homeassistant repository."""

hassio/const.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""Const file for HassIO."""
2-
HASSIO_VERSION = '0.6'
2+
HASSIO_VERSION = '0.7'
33

44
URL_HASSIO_VERSION = \
55
'https://raw.githubusercontent.com/pvizeli/hassio/master/version.json'
@@ -15,6 +15,8 @@
1515
RUN_UPDATE_INFO_TASKS = 28800
1616
RUN_RELOAD_ADDONS_TASKS = 28800
1717

18+
RESTART_EXIT_CODE = 100
19+
1820
FILE_HASSIO_ADDONS = "{}/addons.json".format(HASSIO_SHARE)
1921
FILE_HASSIO_CONFIG = "{}/config.json".format(HASSIO_SHARE)
2022

@@ -49,7 +51,9 @@
4951
STARTUP_BEFORE = 'before'
5052
STARTUP_AFTER = 'after'
5153
STARTUP_ONCE = 'once'
54+
5255
BOOT_AUTO = 'auto'
5356
BOOT_MANUAL = 'manual'
57+
5458
STATE_STARTED = 'started'
5559
STATE_STOPPED = 'stopped'

hassio/core.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class HassIO(object):
2525

2626
def __init__(self, loop):
2727
"""Initialize hassio object."""
28+
self.exit_code = 0
2829
self.loop = loop
2930
self.websession = aiohttp.ClientSession(loop=self.loop)
3031
self.config = bootstrap.initialize_system_data(self.websession)
@@ -35,7 +36,7 @@ def __init__(self, loop):
3536

3637
# init basic docker container
3738
self.supervisor = DockerSupervisor(
38-
self.config, self.loop, self.dock)
39+
self.config, self.loop, self.dock, self)
3940
self.homeassistant = DockerHomeAssistant(
4041
self.config, self.loop, self.dock)
4142

@@ -49,6 +50,7 @@ async def setup(self):
4950
"""Setup HassIO orchestration."""
5051
# supervisor
5152
await self.supervisor.attach()
53+
await self.supervisor.cleanup()
5254

5355
# hostcontroll
5456
host_info = await self.host_controll.info()
@@ -63,7 +65,7 @@ async def setup(self):
6365
# rest api views
6466
self.api.register_host(self.host_controll)
6567
self.api.register_network(self.host_controll)
66-
self.api.register_supervisor(self.host_controll, self.addons)
68+
self.api.register_supervisor(self.supervisor, self.addons)
6769
self.api.register_homeassistant(self.homeassistant)
6870
self.api.register_addons(self.addons)
6971

@@ -104,11 +106,12 @@ async def start(self):
104106
# start addon mark as after
105107
await self.addons.auto_boot(STARTUP_AFTER)
106108

107-
async def stop(self):
109+
async def stop(self, exit_code=0):
108110
"""Stop a running orchestration."""
109111
tasks = [self.websession.close(), self.api.stop()]
110112
await asyncio.wait(tasks, loop=self.loop)
111113

114+
self.exit_code = exit_code
112115
self.loop.stop()
113116

114117
async def _setup_homeassistant(self):

hassio/dock/__init__.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,7 @@ def _remove(self):
209209
return True
210210

211211
async def update(self, tag):
212-
"""Update a docker image.
213-
214-
Return a Future.
215-
"""
212+
"""Update a docker image."""
216213
if self._lock.locked():
217214
_LOGGER.error("Can't excute update while a task is in progress")
218215
return False

hassio/dock/supervisor.py

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,68 @@
11
"""Init file for HassIO docker object."""
2+
import logging
23
import os
34

5+
import docker
6+
47
from . import DockerBase
8+
from ..const import RESTART_EXIT_CODE
9+
10+
_LOGGER = logging.getLogger(__name__)
511

612

713
class DockerSupervisor(DockerBase):
814
"""Docker hassio wrapper for HomeAssistant."""
915

16+
def __init__(self, config, loop, dock, hassio, image=None):
17+
"""Initialize docker base wrapper."""
18+
super().__init__(config, loop, dock, image=image)
19+
20+
self.hassio = hassio
21+
1022
@property
1123
def docker_name(self):
1224
"""Return name of docker container."""
1325
return os.environ['SUPERVISOR_NAME']
1426

27+
async def update(self, tag):
28+
"""Update a supervisor docker image."""
29+
if self._lock.locked():
30+
_LOGGER.error("Can't excute update while a task is in progress")
31+
return False
32+
33+
_LOGGER.info("Update supervisor docker to %s:%s", self.image, tag)
34+
old_version = self.version
35+
36+
async with self._lock:
37+
if await self.loop.run_in_executor(None, self._install, tag):
38+
self.config.hassio_cleanup = old_version
39+
self.loop.create_task(self.hassio.stop(RESTART_EXIT_CODE))
40+
41+
async def cleanup(self):
42+
"""Check if old supervisor version exists and cleanup."""
43+
if not self.config.hassio_cleanup:
44+
return
45+
46+
async with self._lock:
47+
if await self.loop.run_in_executor(None, self._cleanup):
48+
self.config.hassio_cleanup = None
49+
50+
def _cleanup(self):
51+
"""Remove old image.
52+
53+
Need run inside executor.
54+
"""
55+
old_image = "{}:{}".format(self.image, self.config.hassio_cleanup)
56+
57+
_LOGGER.info("Old supervisor docker found %s", old_image)
58+
try:
59+
self.dock.images.remove(image=old_image, force=True)
60+
except docker.errors.DockerException as err:
61+
_LOGGER.warning("Can't remove old image %s -> %s", old_image, err)
62+
return False
63+
64+
return True
65+
1566
async def run(self):
1667
"""Run docker image."""
1768
raise RuntimeError("Not support on supervisor docker container!")
@@ -24,10 +75,6 @@ async def stop(self):
2475
"""Stop/remove docker container."""
2576
raise RuntimeError("Not support on supervisor docker container!")
2677

27-
async def update(self, tag):
28-
"""Update docker image."""
29-
raise RuntimeError("Not support on supervisor docker container!")
30-
3178
async def remove(self):
3279
"""Remove docker image."""
3380
raise RuntimeError("Not support on supervisor docker container!")

hassio/host_controll.py

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@
1414
TIMEOUT = 15
1515

1616
LEVEL_POWER = 1
17-
LEVEL_UPDATE_SUPERVISOR = 2
18-
LEVEL_UPDATE_HOST = 4
19-
LEVEL_NETWORK = 8
17+
LEVEL_UPDATE_HOST = 2
18+
LEVEL_NETWORK = 4
2019

2120

2221
class HostControll(object):
@@ -101,12 +100,3 @@ def host_update(self, version=None):
101100
if version:
102101
return self._send_command("host-update {}".format(version))
103102
return self._send_command("host-update")
104-
105-
def supervisor_update(self, version=None):
106-
"""Update the supervisor on host system.
107-
108-
Return a coroutine.
109-
"""
110-
if version:
111-
return self._send_command("supervisor-update {}".format(version))
112-
return self._send_command("supervisor-update")

version.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"hassio_tag": "0.6",
2+
"hassio_tag": "0.7",
33
"homeassistant_tag": "0.42.3",
44
"resinos_version": "0.3",
55
"resinhup_version": "0.1"

version_beta.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"hassio_tag": "0.6",
2+
"hassio_tag": "0.7",
33
"homeassistant_tag": "0.42.3",
44
"resinos_version": "0.3",
55
"resinhup_version": "0.1"

0 commit comments

Comments
 (0)