diff --git a/packages/services/Makefile b/packages/services/Makefile new file mode 100644 index 000000000..5391de6d2 --- /dev/null +++ b/packages/services/Makefile @@ -0,0 +1,32 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=services +PKG_VERSION=$(GIT_COMMIT_DATE)-$(GIT_COMMIT_TSTAMP) +GIT_COMMIT_DATE:=$(shell git log -n 1 --pretty=%ad --date=short . ) +GIT_COMMIT_TSTAMP:=$(shell git log -n 1 --pretty=%at . ) + +include $(INCLUDE_DIR)/package.mk + +define Package/$(PKG_NAME) + SUBMENU:=Services + SECTION:=net + CATEGORY:=Network + MAINTAINER:=Luandro + TITLE:=Service discovery + DEPENDS:= + PKGARCH:=all +endef + +define Package/$(PKG_NAME)/description + Services discovery for Community Networks. +endef + +define Build/Compile +endef + +define Package/$(PKG_NAME)/install + $(INSTALL_DIR) $(1)/ + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,$(PKG_NAME))) diff --git a/packages/services/README.md b/packages/services/README.md new file mode 100644 index 000000000..7955ae4d0 --- /dev/null +++ b/packages/services/README.md @@ -0,0 +1,36 @@ +# Discoverable Services + +## What + +Exposes a `name`, `description`, `icon` and `ui` (port and path) for each service which had `APP_NAME`, `APP_DESCRIPTION`, `APP_ICON`, `APP_UI` addded to their [Balena environments](). + +Should also accept other formats for Docker, docker-compose or native server setups, according to a manifest format. + +## Architecture + +1. Check connect clients on `/tmp/dhcp.leases` +1. For each client do `wget` on port `48484` +1. If json reponse, save it to `/tmp/services/{ip}.json` +1. Parse json and save a pruned version to `/www/cgi-bin/services` +1. Lime-App or other community-portals can easily fetch services data from `thisnode.info/www/cgi-bin/services` + +## Opinions +- [Balena](https://balena.io) will be highly encouraged because: + - [Balena Hub](https://hub.balena.io/) has a growing number of out-of-the-box apps + - [Balena Cloud](https://balena-cloud.com) is free as long as project is published and used from Balena Hub + - simple migration from existing docker/docker-compose stack + - lightweight container-based operating system + - support for various types of single-board-computers and architectures + - over-the-air-updates + - small self-updating images + - per-device release pinning + - bulk monitoring of all devices and their services + - remote access to all devices and services via web terminal + - ssh tunnel & remote support +- Other docker stacks can work by adding an additional thin httpd image exposing the services manifest +- Native stacks can work by exposing services manifest + +## Challenges + +- Offline + - [Balena offline updates](https://www.balena.io/blog/offline-updates-make-it-easier-to-update-balena-devices-without-the-internet/) \ No newline at end of file diff --git a/packages/services/files/etc/config/services b/packages/services/files/etc/config/services new file mode 100644 index 000000000..e1d2eaba3 --- /dev/null +++ b/packages/services/files/etc/config/services @@ -0,0 +1,2 @@ +config base_config 'base_config' + option enabled '0' diff --git a/packages/services/files/etc/init.d/services b/packages/services/files/etc/init.d/services new file mode 100755 index 000000000..9923cf9bd --- /dev/null +++ b/packages/services/files/etc/init.d/services @@ -0,0 +1,21 @@ +#!/bin/sh /etc/rc.common + +START=99 +NAME=services +PROG=/usr/bin/services + +start() { + if [ $(uci get services.base_config.enabled) == 1 ]; then + logger -t services "Running services" + $PROG + # start + else + logger -t services "Services is disabled" + fi +} + +stop () { + # stop + $PROG stop + logger -t pirania "Services stopped" +} diff --git a/packages/services/files/etc/uci-defaults/90-services-cron b/packages/services/files/etc/uci-defaults/90-services-cron new file mode 100755 index 000000000..51ab0ffed --- /dev/null +++ b/packages/services/files/etc/uci-defaults/90-services-cron @@ -0,0 +1,12 @@ +#!/bin/sh + +unique_append() +{ + grep -qF "$1" "$2" || echo "$1" >> "$2" +} + +unique_append \ + '*/10 * * * * ((services update &> /dev/null)&)'\ + /etc/crontabs/root + +exit 0 diff --git a/packages/services/files/usr/bin/services b/packages/services/files/usr/bin/services new file mode 100755 index 000000000..e54961f4b --- /dev/null +++ b/packages/services/files/usr/bin/services @@ -0,0 +1,24 @@ +#!/bin/sh + +update_services () { +} + +# check if services are enabled in /etc/config/services +enabled=$(uci get services.base_config.enabled) + +if [ "$1" = "start" ]; then + echo "Running services" + update_services + exit +elif [ "$1" = "update" ] ; then + update_services + exit +elif [ "$enabled" = "1" ]; then + update_services + exit +else + echo "Services are disabled. Try running services start" + exit + +fi + diff --git a/packages/services/files/usr/lib/lua/services/main.lua b/packages/services/files/usr/lib/lua/services/main.lua new file mode 100644 index 000000000..b6d686352 --- /dev/null +++ b/packages/services/files/usr/lib/lua/services/main.lua @@ -0,0 +1,28 @@ +#!/bin/lua + +local utils = require('lime.utils') + +function services.list() + local services = {} + -- clients = read('/etc/dhcp.leases').get_ip() + -- mkirp /tmp/services + -- data = [] + -- for client in $(clients) ; do + -- wget $client/v2/local/target-state proxy=on http_proxy=$client:48484 -O /tmp/services/$client.json + -- uci pirania set whitelist for $client? + -- done + -- for $server in $(/tmp/services/*) + -- finalServer = {} + -- services = [] + -- server = json.parse(server) + -- finalServer.name = server.name + -- finalServer.id = server.id + -- finalServer.device = server.device + -- server.apps.map(app => finalServer.apps = name, ui, service, description, icon) + -- finalServer.apps = services + -- data.push(finalServer) + -- done + -- write(data, '/www/cgi-bin/services') +end + +return services diff --git a/packages/services/files/usr/libexec/rpcd/services b/packages/services/files/usr/libexec/rpcd/services new file mode 100755 index 000000000..c2e343a2d --- /dev/null +++ b/packages/services/files/usr/libexec/rpcd/services @@ -0,0 +1,43 @@ +#!/usr/bin/env lua +--[[ +Copyright 2022 Luandro +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-3.0 +]]-- +local ubus = require "ubus" +local json = require 'luci.jsonc' +local uci = require 'uci' +local services = require('services.main') +local utils = require('lime.utils') +local config = require('lime.config') + +local uci_cursor = config.get_uci_cursor() + +local conn = ubus.connect() +if not conn then + error("Failed to connect to ubus") +end + +local function list_services() + -- services.list() + return utils.printJson({ status = 'ok', services = {}}) + -- return utils.printJson({ status = services and 'ok' or 'error', services = services }) +end + +local methods = { + list = { no_params = 0 }, +} + +if arg[1] == 'list' then + utils.printJson(methods) +end + +if arg[1] == 'call' then + local msg = utils.rpcd_readline() + msg = json.parse(msg) + if arg[2] == 'list' then list_services(msg) + else utils.printJson({ error = "Method not found" }) + end +end diff --git a/packages/services/files/usr/share/rpcd/acl.d/services.json b/packages/services/files/usr/share/rpcd/acl.d/services.json new file mode 100644 index 000000000..e4355e6b9 --- /dev/null +++ b/packages/services/files/usr/share/rpcd/acl.d/services.json @@ -0,0 +1,31 @@ +{ + "unauthenticated": { + "description": "services voucher public access", + "write": { + "ubus": { + "services": ["list"] + } + } + }, + "lime-app": { + "description": "lime-app public access", + "read": { + "ubus": { + "services": ["list"] + } + } + }, + "root": { + "description": "services administration access", + "write": { + "ubus": { + "services": ["*"] + } + }, + "read": { + "ubus": { + "services": ["*"] + } + } + } +}