def build(args): global workdir ports = set() deps = set() parser = argparse.ArgumentParser(prog='platydock build', description='Build a Platypush image ' + 'from a config.yaml') parser.add_argument('-c', '--config', type=str, required=True, help='Path to the platypush configuration file') opts, args = parser.parse_known_args(args) cfgfile = os.path.abspath(os.path.expanduser(opts.config)) Config.init(cfgfile) register_backends() backend_config = Config.get_backends() if backend_config.get('http'): http_backend = get_backend('http') ports.add(http_backend.port) if http_backend.websocket_port: ports.add(http_backend.websocket_port) if backend_config.get('tcp'): ports.add(get_backend('tcp').port) if backend_config.get('websocket'): ports.add(get_backend('websocket').port) for name in Config.get_backends().keys(): deps.update(_parse_deps(get_backend(name))) for name in Config.get_plugins().keys(): try: deps.update(_parse_deps(get_plugin(name))) except: pass devdir = os.path.join(workdir, Config.get('device_id')) generate_dockerfile(deps=deps, ports=ports, cfgfile=cfgfile, devdir=devdir) subprocess.call([ 'docker', 'build', '-t', 'platypush-{}'.format(Config.get('device_id')), devdir ])
def get_manifests_from_conf( conf_file: Optional[str] = None) -> Mapping[str, Manifest]: import platypush from platypush.config import Config conf_args = [] if conf_file: conf_args.append(conf_file) Config.init(*conf_args) app_dir = os.path.dirname(inspect.getfile(platypush)) manifest_files = set() for name in Config.get_backends().keys(): manifest_files.add( os.path.join(app_dir, 'backend', *name.split('.'), 'manifest.yaml')) for name in Config.get_plugins().keys(): manifest_files.add( os.path.join(app_dir, 'plugins', *name.split('.'), 'manifest.yaml')) return { manifest_file: Manifest.from_file(manifest_file) for manifest_file in manifest_files }
def build(args): global workdir ports = set() parser = argparse.ArgumentParser( prog='platydock build', description='Build a Platypush image from a config.yaml') parser.add_argument('-c', '--config', type=str, required=True, help='Path to the platypush configuration file') parser.add_argument('-p', '--python-version', type=str, default='3.9', help='Python version to be used') opts, args = parser.parse_known_args(args) cfgfile = os.path.abspath(os.path.expanduser(opts.config)) manifest._available_package_manager = 'apt' # Force apt for Debian-based Docker images install_cmds = manifest.get_dependencies_from_conf(cfgfile) python_version = opts.python_version backend_config = Config.get_backends() # Container exposed ports if backend_config.get('http'): from platypush.backend.http import HttpBackend # noinspection PyProtectedMember ports.add(backend_config['http'].get('port', HttpBackend._DEFAULT_HTTP_PORT)) # noinspection PyProtectedMember ports.add(backend_config['http'].get( 'websocket_port', HttpBackend._DEFAULT_WEBSOCKET_PORT)) if backend_config.get('tcp'): ports.add(backend_config['tcp']['port']) if backend_config.get('websocket'): from platypush.backend.websocket import WebsocketBackend # noinspection PyProtectedMember ports.add(backend_config['websocket'].get( 'port', WebsocketBackend._default_websocket_port)) dev_dir = os.path.join(workdir, Config.get('device_id')) generate_dockerfile(deps=dict(install_cmds), ports=ports, cfgfile=cfgfile, device_dir=dev_dir, python_version=python_version) subprocess.call([ 'docker', 'build', '-t', 'platypush-{}'.format(Config.get('device_id')), dev_dir ])
def notify_web_clients(self, event): backends = Config.get_backends() if 'http' not in backends: return backend = get_backend('http') if backend: backend.notify_web_clients(event) backend = get_backend('websocket') if backend: backend.notify_web_clients(event)
def index(): """ Route to the main web panel """ configured_plugins = Config.get_plugins() enabled_templates = {} enabled_scripts = {} enabled_styles = {} js_folder = os.path.abspath( os.path.join(template_folder, '..', 'static', 'js')) style_folder = os.path.abspath( os.path.join(template_folder, '..', 'static', 'css', 'dist')) for plugin, conf in configured_plugins.items(): template_file = os.path.join(template_folder, 'plugins', plugin, 'index.html') script_file = os.path.join(js_folder, 'plugins', plugin, 'index.js') style_file = os.path.join(style_folder, 'webpanel', 'plugins', plugin + '.css') if os.path.isfile(template_file): conf['_template_file'] = '/' + '/'.join( template_file.split(os.sep)[-3:]) enabled_templates[plugin] = conf if os.path.isfile(script_file): conf['_script_file'] = '/'.join(script_file.split(os.sep)[-4:]) enabled_scripts[plugin] = conf if os.path.isfile(style_file): conf['_style_file'] = 'css/dist/' + style_file[len(style_folder) + 1:] enabled_styles[plugin] = conf http_conf = Config.get('backend.http') return render_template('index.html', templates=enabled_templates, scripts=enabled_scripts, styles=enabled_styles, utils=HttpUtils, token=Config.get('token'), websocket_port=get_websocket_port(), template_folder=template_folder, static_folder=static_folder, plugins=Config.get_plugins(), backends=Config.get_backends(), has_ssl=http_conf.get('ssl_cert') is not None)
def setUp(self): logging.basicConfig(level=logging.INFO, stream=sys.stdout) backends = Config.get_backends() self.assertTrue('http' in backends)
def generate_dockerfile(deps, ports, cfgfile, device_dir, python_version): device_id = Config.get('device_id') if not device_id: raise RuntimeError( ('You need to specify a device_id in {} - Docker ' + 'containers cannot rely on hostname').format(cfgfile)) os.makedirs(device_dir, exist_ok=True) content = textwrap.dedent(''' FROM python:{python_version}-slim-bullseye RUN mkdir -p /app RUN mkdir -p /etc/platypush RUN mkdir -p /usr/local/share/platypush\n '''.format(python_version=python_version)).lstrip() srcdir = os.path.dirname(cfgfile) cfgfile_copy = os.path.join(device_dir, 'config.yaml') shutil.copy(cfgfile, cfgfile_copy, follow_symlinks=True) content += 'COPY config.yaml /etc/platypush/\n' backend_config = Config.get_backends() # Redis configuration for Docker if 'redis' not in backend_config: backend_config['redis'] = { 'redis_args': { 'host': 'redis', 'port': 6379, } } with open(cfgfile_copy, 'a') as f: f.write('\n# Automatically added by platydock, do not remove\n' + yaml.dump({ 'backend.redis': backend_config['redis'], }) + '\n') # Main database configuration has_main_db = False with open(cfgfile_copy, 'r') as f: for line in f.readlines(): if re.match(r'^(main.)?db.*', line): has_main_db = True break if not has_main_db: with open(cfgfile_copy, 'a') as f: f.write( '\n# Automatically added by platydock, do not remove\n' + yaml.dump({'main.db': { 'engine': 'sqlite:////platypush.db', }}) + '\n') # Copy included files # noinspection PyProtectedMember for include in Config._included_files: incdir = os.path.relpath(os.path.dirname(include), srcdir) destdir = os.path.join(device_dir, incdir) pathlib.Path(destdir).mkdir(parents=True, exist_ok=True) shutil.copy(include, destdir, follow_symlinks=True) content += 'RUN mkdir -p /etc/platypush/' + incdir + '\n' content += 'COPY ' + os.path.relpath(include, srcdir) + \ ' /etc/platypush/' + incdir + '\n' # Copy script files scripts_dir = os.path.join(os.path.dirname(cfgfile), 'scripts') if os.path.isdir(scripts_dir): local_scripts_dir = os.path.join(device_dir, 'scripts') remote_scripts_dir = '/etc/platypush/scripts' shutil.copytree(scripts_dir, local_scripts_dir, symlinks=True, dirs_exist_ok=True) content += f'RUN mkdir -p {remote_scripts_dir}\n' content += f'COPY scripts/ {remote_scripts_dir}\n' packages = deps.pop('packages', None) pip = deps.pop('pip', None) exec_cmds = deps.pop('exec', None) pkg_cmd = f'\n\t&& apt-get install --no-install-recommends -y {" ".join(packages)} \\' if packages else '' pip_cmd = f'\n\t&& pip install {" ".join(pip)} \\' if pip else '' content += f''' RUN dpkg --configure -a \\ && apt-get -f install \\ && apt-get --fix-missing install \\ && apt-get clean \\ && apt-get update \\ && apt-get -y upgrade \\ && apt-get -y dist-upgrade \\ && apt-get install --no-install-recommends -y apt-utils \\ && apt-get install --no-install-recommends -y build-essential \\ && apt-get install --no-install-recommends -y git \\ && apt-get install --no-install-recommends -y sudo \\ && apt-get install --no-install-recommends -y libffi-dev \\ && apt-get install --no-install-recommends -y libcap-dev \\ && apt-get install --no-install-recommends -y libjpeg-dev \\{pkg_cmd}{pip_cmd}''' for exec_cmd in exec_cmds: content += f'\n\t&& {exec_cmd} \\' content += ''' && apt-get install --no-install-recommends -y zlib1g-dev RUN git clone --recursive https://git.platypush.tech/platypush/platypush.git /app \\ && cd /app \\ && pip install -r requirements.txt RUN apt-get remove -y git \\ && apt-get remove -y build-essential \\ && apt-get remove -y libffi-dev \\ && apt-get remove -y libjpeg-dev \\ && apt-get remove -y libcap-dev \\ && apt-get remove -y zlib1g-dev \\ && apt-get remove -y apt-utils \\ && apt-get clean \\ && apt-get autoremove -y \\ && rm -rf /var/lib/apt/lists/* ''' for port in ports: content += 'EXPOSE {}\n'.format(port) content += textwrap.dedent(''' ENV PYTHONPATH /app:$PYTHONPATH CMD ["python", "-m", "platypush"] ''') dockerfile = os.path.join(device_dir, 'Dockerfile') print('Generating Dockerfile {}'.format(dockerfile)) with open(dockerfile, 'w') as f: f.write(content)
def index(): """ Route to the main web panel """ configured_plugins = Config.get_plugins() enabled_templates = {} enabled_scripts = {} enabled_styles = {} enabled_plugins = set(request.args.get('enabled_plugins', '').split(',')) for plugin in enabled_plugins: if plugin not in configured_plugins: configured_plugins[plugin] = {} configured_plugins['execute'] = {} disabled_plugins = set(request.args.get('disabled_plugins', '').split(',')) js_folder = os.path.abspath( os.path.join(template_folder, '..', 'static', 'js')) style_folder = os.path.abspath( os.path.join(template_folder, '..', 'static', 'css', 'dist')) for plugin, conf in configured_plugins.copy().items(): if plugin in disabled_plugins: if plugin == 'execute': configured_plugins.pop('execute') continue template_file = os.path.join(template_folder, 'plugins', plugin, 'index.html') script_file = os.path.join(js_folder, 'plugins', plugin, 'index.js') style_file = os.path.join(style_folder, 'webpanel', 'plugins', plugin + '.css') if os.path.isfile(template_file): conf['_template_file'] = '/' + '/'.join( template_file.split(os.sep)[-3:]) enabled_templates[plugin] = conf if os.path.isfile(script_file): conf['_script_file'] = '/'.join(script_file.split(os.sep)[-4:]) enabled_scripts[plugin] = conf if os.path.isfile(style_file): conf['_style_file'] = 'css/dist/' + style_file[len(style_folder) + 1:] enabled_styles[plugin] = conf http_conf = Config.get('backend.http') return render_template('index.html', templates=enabled_templates, scripts=enabled_scripts, styles=enabled_styles, utils=HttpUtils, token=Config.get('token'), websocket_port=get_websocket_port(), template_folder=template_folder, static_folder=static_folder, plugins=Config.get_plugins(), backends=Config.get_backends(), procedures=json.dumps(Config.get_procedures(), cls=Message.Encoder), has_ssl=http_conf.get('ssl_cert') is not None)