Support new API for add-on STDIN support (#207)

* Add function to write data to add-on stdin with API

* Update API Doc

* Add to api

* Update addon.py
This commit is contained in:
Pascal Vizeli 2017-10-03 16:44:48 +02:00 committed by GitHub
parent f167197640
commit bd1c8be1e1
7 changed files with 75 additions and 3 deletions

6
API.md
View File

@ -355,6 +355,7 @@ Get all available addons.
"logo": "bool",
"audio": "bool",
"gpio": "bool",
"stdin": "bool",
"hassio_api": "bool",
"homeassistant_api": "bool"
}
@ -395,6 +396,7 @@ Get all available addons.
"logo": "bool",
"hassio_api": "bool",
"homeassistant_api": "bool",
"stdin": "bool",
"webui": "null|http(s)://[HOST]:port/xy/zx",
"gpio": "bool",
"audio": "bool",
@ -458,6 +460,10 @@ Output is the raw Docker log.
Only supported for local build addons
- POST `/addons/{addon}/stdin`
Write data to add-on stdin
## Host Control
Communicate over UNIX socket with a host daemon.

View File

@ -22,7 +22,7 @@ from ..const import (
STATE_STARTED, STATE_STOPPED, STATE_NONE, ATTR_USER, ATTR_SYSTEM,
ATTR_STATE, ATTR_TIMEOUT, ATTR_AUTO_UPDATE, ATTR_NETWORK, ATTR_WEBUI,
ATTR_HASSIO_API, ATTR_AUDIO, ATTR_AUDIO_OUTPUT, ATTR_AUDIO_INPUT,
ATTR_GPIO, ATTR_HOMEASSISTANT_API)
ATTR_GPIO, ATTR_HOMEASSISTANT_API, ATTR_STDIN)
from .util import check_installed
from ..dock.addon import DockerAddon
from ..tools import write_json_file, read_json_file
@ -270,6 +270,11 @@ class Addon(object):
"""Return True if the add-on access to Home-Assistant api proxy."""
return self._mesh[ATTR_HOMEASSISTANT_API]
@property
def with_stdin(self):
"""Return True if the add-on access use stdin input."""
return self._mesh[ATTR_STDIN]
@property
def with_gpio(self):
"""Return True if the add-on access to gpio interface."""
@ -561,6 +566,18 @@ class Addon(object):
await self.docker.run()
return True
@check_installed
async def write_stdin(self, data):
"""Write data to add-on stdin.
Return a coroutine.
"""
if not self.with_stdin:
_LOGGER.error("Add-on don't support write to stdin!")
return False
return await self.docker.write_stdin(data)
@check_installed
async def snapshot(self, tar_file):
"""Snapshot a state of a addon."""

View File

@ -14,7 +14,7 @@ from ..const import (
ATTR_LOCATON, ATTR_REPOSITORY, ATTR_TIMEOUT, ATTR_NETWORK,
ATTR_AUTO_UPDATE, ATTR_WEBUI, ATTR_AUDIO, ATTR_AUDIO_INPUT,
ATTR_AUDIO_OUTPUT, ATTR_HASSIO_API, ATTR_BUILD_FROM, ATTR_SQUASH,
ATTR_ARGS, ATTR_GPIO, ATTR_HOMEASSISTANT_API)
ATTR_ARGS, ATTR_GPIO, ATTR_HOMEASSISTANT_API, ATTR_STDIN)
from ..validate import NETWORK_PORT, DOCKER_PORTS, ALSA_CHANNEL
@ -98,6 +98,7 @@ SCHEMA_ADDON_CONFIG = vol.Schema({
vol.Optional(ATTR_GPIO, default=False): vol.Boolean(),
vol.Optional(ATTR_HASSIO_API, default=False): vol.Boolean(),
vol.Optional(ATTR_HOMEASSISTANT_API, default=False): vol.Boolean(),
vol.Optional(ATTR_STDIN, default=False): vol.Boolean(),
vol.Required(ATTR_OPTIONS): dict,
vol.Required(ATTR_SCHEMA): vol.Any(vol.Schema({
vol.Coerce(str): vol.Any(SCHEMA_ELEMENT, [

View File

@ -104,6 +104,7 @@ class RestAPI(object):
'/addons/{addon}/rebuild', api_addons.rebuild)
self.webapp.router.add_get('/addons/{addon}/logs', api_addons.logs)
self.webapp.router.add_get('/addons/{addon}/logo', api_addons.logo)
self.webapp.router.add_post('/addons/{addon}/stdin', api_addons.stdin)
def register_security(self):
"""Register security function."""

View File

@ -13,7 +13,7 @@ from ..const import (
ATTR_SOURCE, ATTR_REPOSITORIES, ATTR_ADDONS, ATTR_ARCH, ATTR_MAINTAINER,
ATTR_INSTALLED, ATTR_LOGO, ATTR_WEBUI, ATTR_DEVICES, ATTR_PRIVILEGED,
ATTR_AUDIO, ATTR_AUDIO_INPUT, ATTR_AUDIO_OUTPUT, ATTR_HASSIO_API,
ATTR_GPIO, ATTR_HOMEASSISTANT_API, BOOT_AUTO, BOOT_MANUAL,
ATTR_GPIO, ATTR_HOMEASSISTANT_API, ATTR_STDIN, BOOT_AUTO, BOOT_MANUAL,
CONTENT_TYPE_PNG, CONTENT_TYPE_BINARY)
from ..validate import DOCKER_PORTS
@ -78,6 +78,7 @@ class APIAddons(object):
ATTR_DEVICES: self._pretty_devices(addon),
ATTR_URL: addon.url,
ATTR_LOGO: addon.with_logo,
ATTR_STDIN: addon.with_stdin,
ATTR_HASSIO_API: addon.access_hassio_api,
ATTR_HOMEASSISTANT_API: addon.access_homeassistant_api,
ATTR_AUDIO: addon.with_audio,
@ -129,6 +130,7 @@ class APIAddons(object):
ATTR_DEVICES: self._pretty_devices(addon),
ATTR_LOGO: addon.with_logo,
ATTR_WEBUI: addon.webui,
ATTR_STDIN: addon.with_stdin,
ATTR_HASSIO_API: addon.access_hassio_api,
ATTR_HOMEASSISTANT_API: addon.access_homeassistant_api,
ATTR_GPIO: addon.with_gpio,
@ -242,3 +244,13 @@ class APIAddons(object):
with addon.path_logo.open('rb') as png:
return png.read()
@api_process
async def stdin(self, request):
"""Write to stdin of addon."""
addon = self._extract_addon(request)
if not addon.with_stdin:
raise RuntimeError("STDIN not supported by addons")
data = await request.read()
return asyncio.shield(addon.write_stdin(data), loop=self.loop)

View File

@ -86,6 +86,7 @@ ATTR_STATE = 'state'
ATTR_SCHEMA = 'schema'
ATTR_IMAGE = 'image'
ATTR_LOGO = 'logo'
ATTR_STDIN = 'stdin'
ATTR_ADDONS_REPOSITORIES = 'addons_repositories'
ATTR_REPOSITORY = 'repository'
ATTR_REPOSITORIES = 'repositories'

View File

@ -1,5 +1,6 @@
"""Init file for HassIO addon docker object."""
import logging
import os
import docker
import requests
@ -171,6 +172,7 @@ class DockerAddon(DockerInterface):
name=self.name,
hostname=self.hostname,
detach=True,
stdin_open=self.addon.with_stdin,
network_mode=self.network_mode,
ports=self.ports,
extra_hosts=self.network_mapping,
@ -278,3 +280,35 @@ class DockerAddon(DockerInterface):
"""
self._stop()
return self._run()
@docker_process
def write_stdin(self, data):
"""Write to add-on stdin."""
return self.loop.run_in_executor(None, self._write_stdin, data)
def _write_stdin(self, data):
"""Write to add-on stdin.
Need run inside executor.
"""
if not self._is_running():
return False
try:
# load needed docker objects
container = self.docker.containers.get(self.name)
socket = container.attach_socket(params={'stdin': 1, 'stream': 1})
except docker.errors.DockerException as err:
_LOGGER.error("Can't attach to %s stdin -> %s", self.name, err)
return False
try:
# write to stdin
data += b"\n"
os.write(socket.fileno(), data)
socket.close()
except OSError as err:
_LOGGER.error("Can't write to %s stdin -> %s", self.name, err)
return False
return True