Skip to content

Commit

Permalink
Merge pull request #82 from sabeechen/ingress
Browse files Browse the repository at this point in the history
Version 0.98.1
  • Loading branch information
sabeechen authored Jul 31, 2019
2 parents cbc2416 + cedddb5 commit 0706553
Show file tree
Hide file tree
Showing 26 changed files with 404 additions and 184 deletions.
4 changes: 2 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"python.testing.pyTestArgs": [
"python.testing.pytestArgs": [
"-s",
"--disable-warnings",
"hassio-google-drive-backup"
],
"python.testing.unittestEnabled": false,
"python.testing.nosetestsEnabled": false,
"python.testing.pyTestEnabled": true,
"python.testing.pytestEnabled": true,
"python.pythonPath": "/usr/bin/python3.7",
"python.linting.flake8Enabled": true,
"python.linting.pylintEnabled": false,
Expand Down
6 changes: 6 additions & 0 deletions hassio-google-drive-backup/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## [0.98.1] 2018-07-31
### Added
- Support for Ingress. When upgrading from an older version, the UI will present a dialog asking if you'd still like to serve the Web UI over another port or just use ingress in the future. New users will be ingress-only by default (this can be changed form the settings).
- Support for folders and snapshots in Team Drives.
- Settings option to disable uploads to Google Drive, for example if you just want to use the addon to create snapshots.

## [0.97.4] - 2019-05-29
- Fixes an issue where failed logins caused sync to fail.

Expand Down
3 changes: 0 additions & 3 deletions hassio-google-drive-backup/backup/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,6 @@ def update(self, new_config):
def warnExposeIngressUpgrade(self):
return False

def useIngress(self):
return False

def warnIngress(self):
return False

Expand Down
3 changes: 2 additions & 1 deletion hassio-google-drive-backup/backup/dev/data/dev_options.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
"secrets_file_path": "backup/dev/data/secrets.yaml",
"credentials_file_path": "backup/dev/data/credentials.dat",
"folder_file_path": "backup/dev/data/folder.dat",
"authenticate_url": "http://localhost:2567/external/drivecreds/"
"authenticate_url": "http://localhost:2567/external/drivecreds/",
"ingress_token_file_path": "backup/dev/data/ingress.dat"
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
"keyfile": "backup/dev/ssl/privkey.pem",
"secrets_file_path": "backup/dev/data/secrets.yaml",
"credentials_file_path": "backup/dev/data/credentials.dat",
"folder_file_path": "backup/dev/data/folder.dat"
"folder_file_path": "backup/dev/data/folder.dat",
"ingress_token_file_path": "backup/dev/data/ingress.dat"
}
2 changes: 1 addition & 1 deletion hassio-google-drive-backup/backup/dev/data/options.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"send_error_reports": true,
"max_snapshots_in_hassio": 3,
"max_snapshots_in_hassio": 4,
"max_snapshots_in_google_drive": 3,
"days_between_snapshots": 10,
"use_ssl": false,
Expand Down
2 changes: 1 addition & 1 deletion hassio-google-drive-backup/backup/dev/testbackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ def defaultSettings(self):
"ssl": False,
"watchdog": True,
"wait_boot": 600,
"web_ui": "http://[HOST]:1627/",
"web_ui": "http://[HOST]:8099/",
"ingress_url": "/index",
"supervisor": "2.2.2",
"homeassistant": "0.93.1",
Expand Down
3 changes: 1 addition & 2 deletions hassio-google-drive-backup/backup/globalinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@ def __init__(self, time: Time):
self._syncs = 0
self._failures = 0
self._last_error = 0
self._last_success = None
self._successes = 0
self._last_failure_time = None
self._uploads = 0
self._last_upload = None
self._last_success = None
self._last_success = time.now()
self._start_time = None
self._last_upload_size = None
self._last_sync_start = None
Expand Down
59 changes: 40 additions & 19 deletions hassio-google-drive-backup/backup/hasource.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os.path
from .snapshots import HASnapshot, Snapshot, AbstractSnapshot
from .config import Config
from .time import Time
Expand All @@ -6,7 +7,7 @@
from threading import Lock, Thread
from .harequests import HaRequests
from .exceptions import LogicError
from .helpers import formatException
from .helpers import formatException, touch
from .exceptions import SnapshotInProgress, UploadFailed, ensureKey
from .globalinfo import GlobalInfo
from .const import SOURCE_HA
Expand Down Expand Up @@ -89,6 +90,10 @@ def __init__(self, config: Config, time: Time, ha: HaRequests, info: GlobalInfo)
self.cached_retention = {}
self._info = info
self.pending_options = {}
self._temporary_extra_server = False

def runTemporaryServer(self):
return self._temporary_extra_server

def check(self) -> bool:
# determine if the pending snapshot has timed out, but not if we're still waiting for the request
Expand Down Expand Up @@ -221,6 +226,20 @@ def retain(self, snapshot: Snapshot, retain: bool) -> None:
def init(self):
self._refreshInfo()

# check if the upgrade file is present.
self._temporary_extra_server = False
if not os.path.exists(self.config.get(Setting.INGRESS_TOKEN_FILE_PATH)):
# No upgrade file, so check if drive creds are saved.
if os.path.exists(self.config.get(Setting.CREDENTIALS_FILE_PATH)):
# its an upgrade, so add the extra server option.
self._temporary_extra_server = True
else:
# Its a new install, write the upgrde file so we never check again.
touch(self.config.get(Setting.INGRESS_TOKEN_FILE_PATH))

def refresh(self):
self._refreshInfo()

def _refreshInfo(self) -> None:
self.self_info = self.harequests.selfInfo()
self.host_info = self.harequests.info()
Expand All @@ -232,36 +251,38 @@ def _refreshInfo(self) -> None:
self._info.ha_ssl = ensureKey("ssl", self.ha_info, "Home Assistant metadata")
self._info.addons = ensureKey("addons", self.super_info, "Supervisor metadata")
self._info.slug = ensureKey("slug", self.self_info, "addon metdata")
hostname = ensureKey("hostname", self.host_info, "host metadata")
if hostname is not None:
self._info.url = ensureKey("webui", self.self_info, "addon metdata").replace("[HOST]", hostname + ".local")
else:
self._info.url = None
self._info.url = self.getAddonUrl()

self._info.addDebugInfo("self_info", self.self_info)
self._info.addDebugInfo("host_info", self.host_info)
self._info.addDebugInfo("ha_info", self.ha_info)
self._info.addDebugInfo("super_info", self.super_info)

def getAddonUrl(self):
"""
Returns the relative path to the add-on, for the purpose of linking to the add-on page from within Home Assistant.
"""
return "/hassio/ingress/" + str(self._info.slug)

def getFullAddonUrl(self):
return self._haUrl() + "hassio/ingress/" + str(self._info.slug)

def getFullRestoreLink(self):
return self._haUrl() + "hassio/snapshots"

def _haUrl(self):
if self._info.ha_ssl:
protocol = "https://"
else:
protocol = "http://"
return "".join([protocol, "{host}:", str(self._info.ha_port), "/"])

def _validateSnapshot(self, snapshot) -> HASnapshot:
item: HASnapshot = snapshot.getSource(self.name())
if not item:
raise LogicError("Requested to do something with a snapshot from Home Assistant, but the snapshot has no Home Assistant source")
return item

def getIngressUrl(self):
if self.config.useIngress():
try:
if self.ha_info['ssl']:
protocol = "https"
else:
protocol = "http"
return "{0}://{1}:{2}/hassio/ingress/{3}".format(protocol, self.host_info['hostname'], self.ha_info['port'], self.self_info['slug'])
except KeyError:
return "/"
else:
return "/"

def _requestSnapshot(self, *args) -> None:
data = args
options: CreateOptions = data[1]
Expand Down
2 changes: 1 addition & 1 deletion hassio-google-drive-backup/backup/haupdater.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def _stale(self):
return False
if not self._info._last_error:
return False
return self._time.now() > self._info._last_failure_time + timedelta(seconds=self._config.get(Setting.SNAPSHOT_STALE_SECONDS))
return self._time.now() > self._info._last_success + timedelta(seconds=self._config.get(Setting.SNAPSHOT_STALE_SECONDS))

def _state(self):
if self._stale():
Expand Down
5 changes: 5 additions & 0 deletions hassio-google-drive-backup/backup/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,8 @@ def getPingInfo(servers):
response = response[3:].strip()
pings[host][address] = response
return pings


def touch(file):
with open(file, "w"):
pass
6 changes: 4 additions & 2 deletions hassio-google-drive-backup/backup/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ class Setting(Enum):
RETAINED_FILE_PATH = "retained_file_path"
SECRETS_FILE_PATH = "secrets_file_path"
BACKUP_DIRECTORY_PATH = "backup_directory_path"
INGRESS_TOKEN_FILE_PATH = "ingress_token_file_path"

# enpoints
# endpoints
HASSIO_URL = "hassio_url"
DRIVE_URL = "drive_url"
HOME_ASSISTANT_URL = "home_assistant_url"
Expand Down Expand Up @@ -111,7 +112,7 @@ def key(self):
# UI Server settings
Setting.USE_SSL: False,
Setting.REQUIRE_LOGIN: False,
Setting.EXPOSE_EXTRA_SERVER: True,
Setting.EXPOSE_EXTRA_SERVER: False,
Setting.CERTFILE: "/ssl/fullchain.pem",
Setting.KEYFILE: "/ssl/privkey.pem",
Setting.INGRESS_PORT: 8099,
Expand Down Expand Up @@ -147,6 +148,7 @@ def key(self):
Setting.BACKUP_DIRECTORY_PATH: "/backup",
Setting.RETAINED_FILE_PATH: "/data/retained.json",
Setting.SECRETS_FILE_PATH: "/config/secrets.yaml",
Setting.INGRESS_TOKEN_FILE_PATH: "/data/ingress.dat",

# Various timeouts and intervals
Setting.SNAPSHOT_STALE_SECONDS: 60 * 60 * 3,
Expand Down
1 change: 1 addition & 0 deletions hassio-google-drive-backup/backup/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ def config(cleandir, drive_creds: OAuth2Credentials):
config.override(Setting.CREDENTIALS_FILE_PATH, "credentials.dat")
config.override(Setting.FOLDER_FILE_PATH, "folder.dat")
config.override(Setting.RETAINED_FILE_PATH, "retained.json")
config.override(Setting.INGRESS_TOKEN_FILE_PATH, "ingress.dat")

return config

Expand Down
4 changes: 0 additions & 4 deletions hassio-google-drive-backup/backup/tests/helpers.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
from datetime import datetime
from io import BytesIO
from typing import Dict
from ..snapshots import Snapshot, DummySnapshotSource
from ..model import CreateOptions, SnapshotSource
from ..simulation import SimulatedSource
from threading import Thread, Event
from io import IOBase
import tarfile
import json

Expand Down
53 changes: 49 additions & 4 deletions hassio-google-drive-backup/backup/tests/test_hasource.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import pytest
from os.path import exists
from os import remove
from requests.exceptions import HTTPError
from ..hasource import HaSource, PendingSnapshot
from ..snapshots import HASnapshot, DummySnapshot
Expand Down Expand Up @@ -408,10 +410,53 @@ def test_delete_error(time, ha: HaSource, server: ServerInstance):


def test_hostname(time, ha: HaSource, server: ServerInstance, global_info: GlobalInfo):
server.getServer().update({"hostname": "testme", "web_ui": "http://[HOST]:12345/"})
ha.init()
assert global_info.url == "http://testme.local:12345/"
assert global_info.url == "/hassio/ingress/self_slug"

server.getServer().update({"hostname": None})

def test_ingress_upgrade(time, ha: HaSource, config: Config):
# check the default before init
assert exists(config.get(Setting.CREDENTIALS_FILE_PATH))
assert not exists(config.get(Setting.INGRESS_TOKEN_FILE_PATH))
assert not ha.runTemporaryServer()
ha.init()

# should run the server, since this is an upgrade
assert ha.runTemporaryServer()
assert not exists(config.get(Setting.INGRESS_TOKEN_FILE_PATH))

ha.init()
assert ha.runTemporaryServer()
assert not exists(config.get(Setting.INGRESS_TOKEN_FILE_PATH))


def test_ingress_upgrade_new_install(time, ha: HaSource, config: Config):
# check the default before init
remove(config.get(Setting.CREDENTIALS_FILE_PATH))
assert not exists(config.get(Setting.CREDENTIALS_FILE_PATH))
assert not exists(config.get(Setting.INGRESS_TOKEN_FILE_PATH))
assert not ha.runTemporaryServer()
ha.init()

# should run the server, since this is an upgrade
assert not ha.runTemporaryServer()
assert exists(config.get(Setting.INGRESS_TOKEN_FILE_PATH))

ha.init()
assert not ha.runTemporaryServer()
assert exists(config.get(Setting.INGRESS_TOKEN_FILE_PATH))


def test_ingress_upgrade_file_exists(time, ha: HaSource, config: Config):
with open(config.get(Setting.INGRESS_TOKEN_FILE_PATH), "x"):
pass

# check the default before init
assert exists(config.get(Setting.CREDENTIALS_FILE_PATH))
assert exists(config.get(Setting.INGRESS_TOKEN_FILE_PATH))
assert not ha.runTemporaryServer()
ha.init()
assert global_info.url is None

# should run the server, since this is an upgrade
assert not ha.runTemporaryServer()
assert exists(config.get(Setting.INGRESS_TOKEN_FILE_PATH))
21 changes: 21 additions & 0 deletions hassio-google-drive-backup/backup/tests/test_haupdater.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,27 @@ def test_notification_clears(updater: HaUpdater, server, time: FakeTime, global_
assert backend.getNotification() is None


def test_publish_for_failure(updater: HaUpdater, server, time: FakeTime, global_info: GlobalInfo):
backend: TestBackend = server.getServer()
global_info.success()
updater.update()
assert backend.getNotification() is None

time.advanceDay()
global_info.failed(Exception())
updater.update()
assert backend.getNotification() is not None

time.advanceDay()
global_info.failed(Exception())
updater.update()
assert backend.getNotification() is not None

global_info.success()
updater.update()
assert backend.getNotification() is None


def verifyEntity(backend, name, state, attributes):
assert backend.getEntity(name) == state
assert backend.getAttributes(name) == attributes
Loading

0 comments on commit 0706553

Please sign in to comment.