Skip to content

Commit

Permalink
Merge pull request #4 from pvizeli/selfupdate
Browse files Browse the repository at this point in the history
Allow supervisor to update itself.
  • Loading branch information
pvizeli authored Apr 18, 2017
2 parents 78d1e1d + e1028d6 commit 71590f9
Show file tree
Hide file tree
Showing 12 changed files with 97 additions and 39 deletions.
6 changes: 2 additions & 4 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ Communicate over unix socket with a host daemon.
# reboot
# shutdown
# host-update [v]
# supervisor-update [v]
# network info
# network hostname xy
Expand All @@ -24,9 +23,8 @@ Communicate over unix socket with a host daemon.

level:
- 1: power functions
- 2: supervisor update
- 4: host update
- 8: network functions
- 2: host update
- 4: network functions

Answer:
```
Expand Down
3 changes: 3 additions & 0 deletions hassio/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import asyncio
import logging
import signal
import sys

import hassio.bootstrap as bootstrap
import hassio.core as core
Expand Down Expand Up @@ -33,4 +34,6 @@

loop.run_forever()
loop.close()

_LOGGER.info("Close Hassio")
sys.exit(hassio.exit_code)
4 changes: 2 additions & 2 deletions hassio/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ def register_network(self, host_controll):
self.webapp.router.add_get('/network/info', api_net.info)
self.webapp.router.add_get('/network/options', api_net.options)

def register_supervisor(self, host_controll, addons):
def register_supervisor(self, supervisor, addons):
"""Register supervisor function."""
api_supervisor = APISupervisor(
self.config, self.loop, host_controll, addons)
self.config, self.loop, supervisor, addons)

self.webapp.router.add_get('/supervisor/ping', api_supervisor.ping)
self.webapp.router.add_get('/supervisor/info', api_supervisor.info)
Expand Down
15 changes: 8 additions & 7 deletions hassio/api/supervisor.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
"""Init file for HassIO supervisor rest api."""
import asyncio
import logging

import voluptuous as vol

from .util import api_process, api_process_hostcontroll, api_validate
from .util import api_process, api_validate
from ..const import (
ATTR_ADDONS, ATTR_VERSION, ATTR_CURRENT, ATTR_BETA, HASSIO_VERSION)

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

def __init__(self, config, loop, host_controll, addons):
def __init__(self, config, loop, supervisor, addons):
"""Initialize supervisor rest api part."""
self.config = config
self.loop = loop
self.host_controll = host_controll
self.supervisor = supervisor
self.addons = addons

@api_process
Expand Down Expand Up @@ -55,13 +56,13 @@ async def options(self, request):

return self.config.save()

@api_process_hostcontroll
@api_process
async def update(self, request):
"""Update host OS."""
"""Update supervisor OS."""
body = await api_validate(SCHEMA_VERSION, request)
version = body.get(ATTR_VERSION, self.config.current_hassio)

if version == HASSIO_VERSION:
if version == self.supervisor.version:
raise RuntimeError("Version is already in use")

return await self.host_controll.supervisor_update(version=version)
return await asyncio.shield(self.supervisor.update(version))
15 changes: 15 additions & 0 deletions hassio/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

HASSIO_SSL = "{}/ssl"
HASSIO_CURRENT = 'hassio_current'
HASSIO_CLEANUP = 'hassio_cleanup'

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

@property
def hassio_cleanup(self):
"""Return Version they need to cleanup."""
return self._data.get(HASSIO_CLEANUP)

@hassio_cleanup.setter
def hassio_cleanup(self, version):
"""Set or remove cleanup flag."""
if version is None:
self._data.pop(HASSIO_CLEANUP, None)
else:
self._data[HASSIO_CLEANUP] = version
self.save()

@property
def homeassistant_image(self):
"""Return docker homeassistant repository."""
Expand Down
6 changes: 5 additions & 1 deletion hassio/const.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Const file for HassIO."""
HASSIO_VERSION = '0.6'
HASSIO_VERSION = '0.7'

URL_HASSIO_VERSION = \
'https://raw.githubusercontent.com/pvizeli/hassio/master/version.json'
Expand All @@ -15,6 +15,8 @@
RUN_UPDATE_INFO_TASKS = 28800
RUN_RELOAD_ADDONS_TASKS = 28800

RESTART_EXIT_CODE = 100

FILE_HASSIO_ADDONS = "{}/addons.json".format(HASSIO_SHARE)
FILE_HASSIO_CONFIG = "{}/config.json".format(HASSIO_SHARE)

Expand Down Expand Up @@ -49,7 +51,9 @@
STARTUP_BEFORE = 'before'
STARTUP_AFTER = 'after'
STARTUP_ONCE = 'once'

BOOT_AUTO = 'auto'
BOOT_MANUAL = 'manual'

STATE_STARTED = 'started'
STATE_STOPPED = 'stopped'
9 changes: 6 additions & 3 deletions hassio/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class HassIO(object):

def __init__(self, loop):
"""Initialize hassio object."""
self.exit_code = 0
self.loop = loop
self.websession = aiohttp.ClientSession(loop=self.loop)
self.config = bootstrap.initialize_system_data(self.websession)
Expand All @@ -35,7 +36,7 @@ def __init__(self, loop):

# init basic docker container
self.supervisor = DockerSupervisor(
self.config, self.loop, self.dock)
self.config, self.loop, self.dock, self)
self.homeassistant = DockerHomeAssistant(
self.config, self.loop, self.dock)

Expand All @@ -49,6 +50,7 @@ async def setup(self):
"""Setup HassIO orchestration."""
# supervisor
await self.supervisor.attach()
await self.supervisor.cleanup()

# hostcontroll
host_info = await self.host_controll.info()
Expand All @@ -63,7 +65,7 @@ async def setup(self):
# rest api views
self.api.register_host(self.host_controll)
self.api.register_network(self.host_controll)
self.api.register_supervisor(self.host_controll, self.addons)
self.api.register_supervisor(self.supervisor, self.addons)
self.api.register_homeassistant(self.homeassistant)
self.api.register_addons(self.addons)

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

async def stop(self):
async def stop(self, exit_code=0):
"""Stop a running orchestration."""
tasks = [self.websession.close(), self.api.stop()]
await asyncio.wait(tasks, loop=self.loop)

self.exit_code = exit_code
self.loop.stop()

async def _setup_homeassistant(self):
Expand Down
5 changes: 1 addition & 4 deletions hassio/dock/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,10 +209,7 @@ def _remove(self):
return True

async def update(self, tag):
"""Update a docker image.
Return a Future.
"""
"""Update a docker image."""
if self._lock.locked():
_LOGGER.error("Can't excute update while a task is in progress")
return False
Expand Down
55 changes: 51 additions & 4 deletions hassio/dock/supervisor.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,68 @@
"""Init file for HassIO docker object."""
import logging
import os

import docker

from . import DockerBase
from ..const import RESTART_EXIT_CODE

_LOGGER = logging.getLogger(__name__)


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

def __init__(self, config, loop, dock, hassio, image=None):
"""Initialize docker base wrapper."""
super().__init__(config, loop, dock, image=image)

self.hassio = hassio

@property
def docker_name(self):
"""Return name of docker container."""
return os.environ['SUPERVISOR_NAME']

async def update(self, tag):
"""Update a supervisor docker image."""
if self._lock.locked():
_LOGGER.error("Can't excute update while a task is in progress")
return False

_LOGGER.info("Update supervisor docker to %s:%s", self.image, tag)
old_version = self.version

async with self._lock:
if await self.loop.run_in_executor(None, self._install, tag):
self.config.hassio_cleanup = old_version
self.loop.create_task(self.hassio.stop(RESTART_EXIT_CODE))

async def cleanup(self):
"""Check if old supervisor version exists and cleanup."""
if not self.config.hassio_cleanup:
return

async with self._lock:
if await self.loop.run_in_executor(None, self._cleanup):
self.config.hassio_cleanup = None

def _cleanup(self):
"""Remove old image.
Need run inside executor.
"""
old_image = "{}:{}".format(self.image, self.config.hassio_cleanup)

_LOGGER.info("Old supervisor docker found %s", old_image)
try:
self.dock.images.remove(image=old_image, force=True)
except docker.errors.DockerException as err:
_LOGGER.warning("Can't remove old image %s -> %s", old_image, err)
return False

return True

async def run(self):
"""Run docker image."""
raise RuntimeError("Not support on supervisor docker container!")
Expand All @@ -24,10 +75,6 @@ async def stop(self):
"""Stop/remove docker container."""
raise RuntimeError("Not support on supervisor docker container!")

async def update(self, tag):
"""Update docker image."""
raise RuntimeError("Not support on supervisor docker container!")

async def remove(self):
"""Remove docker image."""
raise RuntimeError("Not support on supervisor docker container!")
14 changes: 2 additions & 12 deletions hassio/host_controll.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@
TIMEOUT = 15

LEVEL_POWER = 1
LEVEL_UPDATE_SUPERVISOR = 2
LEVEL_UPDATE_HOST = 4
LEVEL_NETWORK = 8
LEVEL_UPDATE_HOST = 2
LEVEL_NETWORK = 4


class HostControll(object):
Expand Down Expand Up @@ -101,12 +100,3 @@ def host_update(self, version=None):
if version:
return self._send_command("host-update {}".format(version))
return self._send_command("host-update")

def supervisor_update(self, version=None):
"""Update the supervisor on host system.
Return a coroutine.
"""
if version:
return self._send_command("supervisor-update {}".format(version))
return self._send_command("supervisor-update")
2 changes: 1 addition & 1 deletion version.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"hassio_tag": "0.6",
"hassio_tag": "0.7",
"homeassistant_tag": "0.42.3",
"resinos_version": "0.3",
"resinhup_version": "0.1"
Expand Down
2 changes: 1 addition & 1 deletion version_beta.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"hassio_tag": "0.6",
"hassio_tag": "0.7",
"homeassistant_tag": "0.42.3",
"resinos_version": "0.3",
"resinhup_version": "0.1"
Expand Down

0 comments on commit 71590f9

Please sign in to comment.