Skip to content

Commit 7186f5a

Browse files
authored
Add more addons functions. (#91)
* Add more addons functions. * fix lint * fix lint p2 * Allow more customable network settings * fix lint * change point of validate * fix lint * fix handling * fix lint & validate data before write
1 parent f52d1c4 commit 7186f5a

File tree

14 files changed

+175
-62
lines changed

14 files changed

+175
-62
lines changed

API.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ Output the raw docker log
303303
{
304304
"name": "xy bla",
305305
"description": "description",
306+
"auto_update": "bool",
306307
"url": "null|url of addon",
307308
"detached": "bool",
308309
"repository": "12345678|null",
@@ -312,17 +313,25 @@ Output the raw docker log
312313
"boot": "auto|manual",
313314
"build": "bool",
314315
"options": "{}",
316+
"network": "{}|null",
317+
"host_network": "bool"
315318
}
316319
```
317320

318321
- POST `/addons/{addon}/options`
319322
```json
320323
{
321324
"boot": "auto|manual",
325+
"auto_update": "bool",
326+
"network": {
327+
"CONTAINER": "port|[ip, port]"
328+
},
322329
"options": {},
323330
}
324331
```
325332

333+
For reset custom network settings, set it `null`.
334+
326335
- POST `/addons/{addon}/start`
327336

328337
- POST `/addons/{addon}/stop`

hassio/addons/addon.py

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,14 @@
1212
from voluptuous.humanize import humanize_error
1313

1414
from .validate import (
15-
validate_options, SCHEMA_ADDON_USER, SCHEMA_ADDON_SYSTEM,
16-
SCHEMA_ADDON_SNAPSHOT, MAP_VOLUME)
15+
validate_options, SCHEMA_ADDON_SNAPSHOT, MAP_VOLUME)
1716
from ..const import (
1817
ATTR_NAME, ATTR_VERSION, ATTR_SLUG, ATTR_DESCRIPTON, ATTR_BOOT, ATTR_MAP,
1918
ATTR_OPTIONS, ATTR_PORTS, ATTR_SCHEMA, ATTR_IMAGE, ATTR_REPOSITORY,
2019
ATTR_URL, ATTR_ARCH, ATTR_LOCATON, ATTR_DEVICES, ATTR_ENVIRONMENT,
2120
ATTR_HOST_NETWORK, ATTR_TMPFS, ATTR_PRIVILEGED, ATTR_STARTUP,
2221
STATE_STARTED, STATE_STOPPED, STATE_NONE, ATTR_USER, ATTR_SYSTEM,
23-
ATTR_STATE)
22+
ATTR_STATE, ATTR_TIMEOUT, ATTR_AUTO_UPDATE, ATTR_NETWORK)
2423
from .util import check_installed
2524
from ..dock.addon import DockerAddon
2625
from ..tools import write_json_file, read_json_file
@@ -45,21 +44,8 @@ def __init__(self, config, loop, dock, data, slug):
4544
async def load(self):
4645
"""Async initialize of object."""
4746
if self.is_installed:
48-
self._validate_system_user()
4947
await self.addon_docker.attach()
5048

51-
def _validate_system_user(self):
52-
"""Validate internal data they read from file."""
53-
for data, schema in ((self.data.system, SCHEMA_ADDON_SYSTEM),
54-
(self.data.user, SCHEMA_ADDON_USER)):
55-
try:
56-
data[self._id] = schema(data[self._id])
57-
except vol.Invalid as err:
58-
_LOGGER.warning("Can't validate addon load %s -> %s", self._id,
59-
humanize_error(data[self._id], err))
60-
except KeyError:
61-
pass
62-
6349
@property
6450
def slug(self):
6551
"""Return slug/id of addon."""
@@ -141,11 +127,27 @@ def boot(self, value):
141127
self.data.user[self._id][ATTR_BOOT] = value
142128
self.data.save()
143129

130+
@property
131+
def auto_update(self):
132+
"""Return if auto update is enable."""
133+
return self.data.user[self._id][ATTR_AUTO_UPDATE]
134+
135+
@auto_update.setter
136+
def auto_update(self, value):
137+
"""Set auto update."""
138+
self.data.user[self._id][ATTR_AUTO_UPDATE] = value
139+
self.data.save()
140+
144141
@property
145142
def name(self):
146143
"""Return name of addon."""
147144
return self._mesh[ATTR_NAME]
148145

146+
@property
147+
def timeout(self):
148+
"""Return timeout of addon for docker stop."""
149+
return self._mesh[ATTR_TIMEOUT]
150+
149151
@property
150152
def description(self):
151153
"""Return description of addon."""
@@ -171,7 +173,28 @@ def startup(self):
171173
@property
172174
def ports(self):
173175
"""Return ports of addon."""
174-
return self._mesh.get(ATTR_PORTS)
176+
if self.network_mode != 'bridge' or ATTR_PORTS not in self._mesh:
177+
return
178+
179+
if not self.is_installed or \
180+
ATTR_NETWORK not in self.data.user[self._id]:
181+
return self._mesh[ATTR_PORTS]
182+
return self.data.user[self._id][ATTR_NETWORK]
183+
184+
@ports.setter
185+
def ports(self, value):
186+
"""Set custom ports of addon."""
187+
if value is None:
188+
self.data.user[self._id].pop(ATTR_NETWORK, None)
189+
else:
190+
new_ports = {}
191+
for container_port, host_port in value.items():
192+
if container_port in self._mesh.get(ATTR_PORTS, {}):
193+
new_ports[container_port] = host_port
194+
195+
self.data.user[self._id][ATTR_NETWORK] = new_ports
196+
197+
self.data.save()
175198

176199
@property
177200
def network_mode(self):

hassio/addons/data.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
from .util import extract_hash_from_path
1212
from .validate import (
13-
SCHEMA_ADDON, SCHEMA_REPOSITORY_CONFIG, MAP_VOLUME)
13+
SCHEMA_ADDON_CONFIG, SCHEMA_ADDON_FILE, SCHEMA_REPOSITORY_CONFIG,
14+
MAP_VOLUME)
1415
from ..const import (
1516
FILE_HASSIO_ADDONS, ATTR_VERSION, ATTR_SLUG, ATTR_REPOSITORY, ATTR_LOCATON,
1617
REPOSITORY_CORE, REPOSITORY_LOCAL, ATTR_USER, ATTR_SYSTEM)
@@ -40,13 +41,23 @@ def __init__(self, config):
4041
_LOGGER.warning("Can't read %s", self._file)
4142
self._data = {}
4243

43-
# init data
44-
if not self._data:
45-
self._data[ATTR_USER] = {}
46-
self._data[ATTR_SYSTEM] = {}
44+
# validate
45+
try:
46+
self._data = SCHEMA_ADDON_FILE(self._data)
47+
except vol.Invalid as ex:
48+
_LOGGER.error("Can't parse addons.json -> %s",
49+
humanize_error(self._data, ex))
4750

4851
def save(self):
4952
"""Store data to config file."""
53+
# validate
54+
try:
55+
self._data = SCHEMA_ADDON_FILE(self._data)
56+
except vol.Invalid as ex:
57+
_LOGGER.error("Can't parse addons data -> %s",
58+
humanize_error(self._data, ex))
59+
return False
60+
5061
if not write_json_file(self._file, self._data):
5162
_LOGGER.error("Can't store config in %s", self._file)
5263
return False
@@ -127,7 +138,7 @@ def _read_addons_folder(self, path, repository):
127138
addon_config = read_json_file(addon)
128139

129140
# validate
130-
addon_config = SCHEMA_ADDON(addon_config)
141+
addon_config = SCHEMA_ADDON_CONFIG(addon_config)
131142

132143
# Generate slug
133144
addon_slug = "{}_{}".format(

hassio/addons/validate.py

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
ATTR_IMAGE, ATTR_URL, ATTR_MAINTAINER, ATTR_ARCH, ATTR_DEVICES,
99
ATTR_ENVIRONMENT, ATTR_HOST_NETWORK, ARCH_ARMHF, ARCH_AARCH64, ARCH_AMD64,
1010
ARCH_I386, ATTR_TMPFS, ATTR_PRIVILEGED, ATTR_USER, ATTR_STATE, ATTR_SYSTEM,
11-
STATE_STARTED, STATE_STOPPED, ATTR_LOCATON, ATTR_REPOSITORY)
11+
STATE_STARTED, STATE_STOPPED, ATTR_LOCATON, ATTR_REPOSITORY, ATTR_TIMEOUT,
12+
ATTR_NETWORK, ATTR_AUTO_UPDATE)
13+
from ..validate import NETWORK_PORT, DOCKER_PORTS
1214

1315

1416
MAP_VOLUME = r"^(config|ssl|addons|backup|share)(?::(rw|:ro))?$"
@@ -19,8 +21,9 @@
1921
V_BOOL = 'bool'
2022
V_EMAIL = 'email'
2123
V_URL = 'url'
24+
V_PORT = 'port'
2225

23-
ADDON_ELEMENT = vol.In([V_STR, V_INT, V_FLOAT, V_BOOL, V_EMAIL, V_URL])
26+
ADDON_ELEMENT = vol.In([V_STR, V_INT, V_FLOAT, V_BOOL, V_EMAIL, V_URL, V_PORT])
2427

2528
ARCH_ALL = [
2629
ARCH_ARMHF, ARCH_AARCH64, ARCH_AMD64, ARCH_I386
@@ -31,16 +34,6 @@
3134
]
3235

3336

34-
def check_network(data):
35-
"""Validate network settings."""
36-
host_network = data[ATTR_HOST_NETWORK]
37-
38-
if ATTR_PORTS in data and host_network:
39-
raise vol.Invalid("Hostnetwork & ports are not allow!")
40-
41-
return data
42-
43-
4437
# pylint: disable=no-value-for-parameter
4538
SCHEMA_ADDON_CONFIG = vol.Schema({
4639
vol.Required(ATTR_NAME): vol.Coerce(str),
@@ -54,7 +47,7 @@ def check_network(data):
5447
STARTUP_INITIALIZE]),
5548
vol.Required(ATTR_BOOT):
5649
vol.In([BOOT_AUTO, BOOT_MANUAL]),
57-
vol.Optional(ATTR_PORTS): dict,
50+
vol.Optional(ATTR_PORTS): DOCKER_PORTS,
5851
vol.Optional(ATTR_HOST_NETWORK, default=False): vol.Boolean(),
5952
vol.Optional(ATTR_DEVICES): [vol.Match(r"^(.*):(.*):([rwm]{1,3})$")],
6053
vol.Optional(ATTR_TMPFS):
@@ -69,8 +62,10 @@ def check_network(data):
6962
])
7063
}, False),
7164
vol.Optional(ATTR_IMAGE): vol.Match(r"\w*/\w*"),
65+
vol.Optional(ATTR_TIMEOUT, default=10):
66+
vol.All(vol.Coerce(int), vol.Range(min=10, max=120))
7267
}, extra=vol.ALLOW_EXTRA)
73-
SCHEMA_ADDON = vol.Schema(vol.All(SCHEMA_ADDON_CONFIG, check_network))
68+
7469

7570
# pylint: disable=no-value-for-parameter
7671
SCHEMA_REPOSITORY_CONFIG = vol.Schema({
@@ -80,11 +75,14 @@ def check_network(data):
8075
}, extra=vol.ALLOW_EXTRA)
8176

8277

78+
# pylint: disable=no-value-for-parameter
8379
SCHEMA_ADDON_USER = vol.Schema({
8480
vol.Required(ATTR_VERSION): vol.Coerce(str),
8581
vol.Required(ATTR_OPTIONS): dict,
82+
vol.Optional(ATTR_AUTO_UPDATE, default=False): vol.Boolean(),
8683
vol.Optional(ATTR_BOOT):
8784
vol.In([BOOT_AUTO, BOOT_MANUAL]),
85+
vol.Optional(ATTR_NETWORK): DOCKER_PORTS,
8886
})
8987

9088

@@ -94,6 +92,16 @@ def check_network(data):
9492
})
9593

9694

95+
SCHEMA_ADDON_FILE = vol.Schema({
96+
vol.Optional(ATTR_USER, default={}): {
97+
vol.Coerce(str): SCHEMA_ADDON_USER,
98+
},
99+
vol.Optional(ATTR_SYSTEM, default={}): {
100+
vol.Coerce(str): SCHEMA_ADDON_SYSTEM,
101+
}
102+
})
103+
104+
97105
SCHEMA_ADDON_SNAPSHOT = vol.Schema({
98106
vol.Required(ATTR_USER): SCHEMA_ADDON_USER,
99107
vol.Required(ATTR_SYSTEM): SCHEMA_ADDON_SYSTEM,
@@ -150,6 +158,8 @@ def _single_validate(typ, value, key):
150158
return vol.Email()(value)
151159
elif typ == V_URL:
152160
return vol.Url()(value)
161+
elif typ == V_PORT:
162+
return NETWORK_PORT(value)
153163

154164
raise vol.Invalid("Fatal error for {} type {}.".format(key, typ))
155165
except ValueError:

hassio/api/addons.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,21 @@
99
from ..const import (
1010
ATTR_VERSION, ATTR_LAST_VERSION, ATTR_STATE, ATTR_BOOT, ATTR_OPTIONS,
1111
ATTR_URL, ATTR_DESCRIPTON, ATTR_DETACHED, ATTR_NAME, ATTR_REPOSITORY,
12-
ATTR_BUILD, BOOT_AUTO, BOOT_MANUAL)
12+
ATTR_BUILD, ATTR_AUTO_UPDATE, ATTR_NETWORK, ATTR_HOST_NETWORK,
13+
BOOT_AUTO, BOOT_MANUAL)
14+
from ..validate import DOCKER_PORTS
1315

1416
_LOGGER = logging.getLogger(__name__)
1517

1618
SCHEMA_VERSION = vol.Schema({
1719
vol.Optional(ATTR_VERSION): vol.Coerce(str),
1820
})
1921

22+
# pylint: disable=no-value-for-parameter
2023
SCHEMA_OPTIONS = vol.Schema({
21-
vol.Optional(ATTR_BOOT): vol.In([BOOT_AUTO, BOOT_MANUAL])
24+
vol.Optional(ATTR_BOOT): vol.In([BOOT_AUTO, BOOT_MANUAL]),
25+
vol.Optional(ATTR_NETWORK): vol.Any(None, DOCKER_PORTS),
26+
vol.Optional(ATTR_AUTO_UPDATE): vol.Boolean(),
2227
})
2328

2429

@@ -51,6 +56,7 @@ async def info(self, request):
5156
ATTR_NAME: addon.name,
5257
ATTR_DESCRIPTON: addon.description,
5358
ATTR_VERSION: addon.version_installed,
59+
ATTR_AUTO_UPDATE: addon.auto_update,
5460
ATTR_REPOSITORY: addon.repository,
5561
ATTR_LAST_VERSION: addon.last_version,
5662
ATTR_STATE: await addon.state(),
@@ -59,6 +65,8 @@ async def info(self, request):
5965
ATTR_URL: addon.url,
6066
ATTR_DETACHED: addon.is_detached,
6167
ATTR_BUILD: addon.need_build,
68+
ATTR_NETWORK: addon.ports,
69+
ATTR_HOST_NETWORK: addon.network_mode == 'host',
6270
}
6371

6472
@api_process
@@ -76,6 +84,10 @@ async def options(self, request):
7684
addon.options = body[ATTR_OPTIONS]
7785
if ATTR_BOOT in body:
7886
addon.boot = body[ATTR_BOOT]
87+
if ATTR_AUTO_UPDATE in body:
88+
addon.auto_update = body[ATTR_AUTO_UPDATE]
89+
if ATTR_NETWORK in body:
90+
addon.ports = body[ATTR_NETWORK]
7991

8092
return True
8193

hassio/api/homeassistant.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66

77
from .util import api_process, api_process_raw, api_validate
88
from ..const import ATTR_VERSION, ATTR_LAST_VERSION, ATTR_DEVICES
9+
from ..validate import HASS_DEVICES
910

1011
_LOGGER = logging.getLogger(__name__)
1112

1213

1314
SCHEMA_OPTIONS = vol.Schema({
14-
vol.Optional(ATTR_DEVICES): [vol.Match(r"^[^/]*$")],
15+
vol.Optional(ATTR_DEVICES): HASS_DEVICES,
1516
})
1617

1718
SCHEMA_VERSION = vol.Schema({

hassio/config.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from .const import FILE_HASSIO_CONFIG, HASSIO_DATA
1212
from .tools import (
1313
fetch_last_versions, write_json_file, read_json_file, validate_timezone)
14+
from .validate import HASS_DEVICES
1415

1516
_LOGGER = logging.getLogger(__name__)
1617

@@ -49,7 +50,7 @@
4950
vol.Optional(API_ENDPOINT): vol.Coerce(str),
5051
vol.Optional(TIMEZONE, default='UTC'): validate_timezone,
5152
vol.Optional(HOMEASSISTANT_LAST): vol.Coerce(str),
52-
vol.Optional(HOMEASSISTANT_DEVICES, default=[]): [vol.Coerce(str)],
53+
vol.Optional(HOMEASSISTANT_DEVICES, default=[]): HASS_DEVICES,
5354
vol.Optional(HASSIO_LAST): vol.Coerce(str),
5455
vol.Optional(ADDONS_CUSTOM_LIST, default=[]): [vol.Url()],
5556
vol.Optional(SECURITY_INITIALIZE, default=False): vol.Boolean(),

hassio/const.py

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

1515
RUN_UPDATE_INFO_TASKS = 28800
1616
RUN_UPDATE_SUPERVISOR_TASKS = 29100
17+
RUN_UPDATE_ADDONS_TASKS = 57600
1718
RUN_RELOAD_ADDONS_TASKS = 28800
1819
RUN_RELOAD_SNAPSHOTS_TASKS = 72000
1920
RUN_WATCHDOG_HOMEASSISTANT = 15
@@ -81,6 +82,7 @@
8182
ATTR_DEVICES = 'devices'
8283
ATTR_ENVIRONMENT = 'environment'
8384
ATTR_HOST_NETWORK = 'host_network'
85+
ATTR_NETWORK = 'network'
8486
ATTR_TMPFS = 'tmpfs'
8587
ATTR_PRIVILEGED = 'privileged'
8688
ATTR_USER = 'user'
@@ -90,6 +92,8 @@
9092
ATTR_FOLDERS = 'folders'
9193
ATTR_SIZE = 'size'
9294
ATTR_TYPE = 'type'
95+
ATTR_TIMEOUT = 'timeout'
96+
ATTR_AUTO_UPDATE = 'auto_update'
9397

9498
STARTUP_INITIALIZE = 'initialize'
9599
STARTUP_BEFORE = 'before'

0 commit comments

Comments
 (0)