Initial tests
This commit is contained in:
parent
d3d22ba677
commit
60aa73126e
16
package/gluon-config-api/Makefile
Normal file
16
package/gluon-config-api/Makefile
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Copyright (C) 2021 Leonardo Moerlein <me at irrelefant.net>
|
||||||
|
# This is free software, licensed under the Apache 2.0 license.
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=gluon-config-api
|
||||||
|
PKG_VERSION:=1
|
||||||
|
|
||||||
|
include ../gluon.mk
|
||||||
|
|
||||||
|
define Package/gluon-config-api
|
||||||
|
TITLE:=Provides a REST API to configure the gluon node
|
||||||
|
DEPENDS:=+gluon-web +uhttpd +libucl
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackageGluon,gluon-config-api))
|
@ -0,0 +1,48 @@
|
|||||||
|
local json = require 'jsonc'
|
||||||
|
local site = require 'gluon.site'
|
||||||
|
local util = require 'gluon.util'
|
||||||
|
local ubus = require 'ubus'
|
||||||
|
local os = require 'os'
|
||||||
|
local glob = require 'posix.glob'
|
||||||
|
local libgen = require 'posix.libgen'
|
||||||
|
local simpleuci = require 'simple-uci'
|
||||||
|
local schema = require 'schema'
|
||||||
|
|
||||||
|
package 'gluon-config-api'
|
||||||
|
|
||||||
|
function load_parts()
|
||||||
|
local parts = {}
|
||||||
|
for _, f in pairs(glob.glob('/lib/gluon/config-api/parts/*.lua')) do
|
||||||
|
table.insert(parts, dofile(f))
|
||||||
|
end
|
||||||
|
return parts
|
||||||
|
end
|
||||||
|
|
||||||
|
function config_get(parts)
|
||||||
|
local config = {}
|
||||||
|
local uci = simpleuci.cursor()
|
||||||
|
|
||||||
|
for _, part in pairs(parts) do
|
||||||
|
part.get(uci, config)
|
||||||
|
end
|
||||||
|
|
||||||
|
return config
|
||||||
|
end
|
||||||
|
|
||||||
|
local parts = load_parts()
|
||||||
|
|
||||||
|
|
||||||
|
entry({"config"}, call(function(http, renderer)
|
||||||
|
|
||||||
|
http:write(json.stringify(config_get(parts), true))
|
||||||
|
http:close()
|
||||||
|
end))
|
||||||
|
|
||||||
|
entry({"schema"}, call(function(http, renderer)
|
||||||
|
local total_schema = {}
|
||||||
|
for _, part in pairs(parts) do
|
||||||
|
total_schema = schema.merge_schemas(total_schema, part.schema())
|
||||||
|
end
|
||||||
|
http:write(json.stringify(total_schema, true))
|
||||||
|
http:close()
|
||||||
|
end))
|
@ -0,0 +1,137 @@
|
|||||||
|
|
||||||
|
local util = require 'gluon.util'
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
local function merge_types(Ta, Tb)
|
||||||
|
-- T == nil means "any" type is allowed
|
||||||
|
|
||||||
|
if not Ta then return Tb end
|
||||||
|
if not Tb then return Ta end
|
||||||
|
|
||||||
|
-- convert scalar types to arrays
|
||||||
|
if type(Ta) ~= 'table' then Ta = { Ta } end
|
||||||
|
if type(Tb) ~= 'table' then Tb = { Tb } end
|
||||||
|
|
||||||
|
local Tnew = {}
|
||||||
|
|
||||||
|
for _, t in pairs(Ta) do
|
||||||
|
if util.contains(Tb, t) then
|
||||||
|
table.insert(Tnew, t)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assert(#Tnew > 0, 'ERROR: The schema does not match anything at all.')
|
||||||
|
|
||||||
|
if #Tnew == 1 then
|
||||||
|
return Tnew[1] -- convert to scalar
|
||||||
|
else
|
||||||
|
return Tnew
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function merged_keys(table1, table2)
|
||||||
|
local keys = {}
|
||||||
|
if table1 then
|
||||||
|
for k, _ in pairs(table1) do
|
||||||
|
table.insert(k)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if table2 then
|
||||||
|
for k, _ in pairs(table2) do
|
||||||
|
if not util.contains(k) then
|
||||||
|
table.insert(keys, k)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return keys
|
||||||
|
end
|
||||||
|
|
||||||
|
local function merged_values(table1, table2)
|
||||||
|
local values = {}
|
||||||
|
if table1 then
|
||||||
|
for _, v in pairs(table1) do
|
||||||
|
table.insert(v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if table2 then
|
||||||
|
for _, v in pairs(table2) do
|
||||||
|
if not util.contains(v) then
|
||||||
|
table.insert(values, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return values
|
||||||
|
end
|
||||||
|
|
||||||
|
local function deepcopy(o, seen)
|
||||||
|
seen = seen or {}
|
||||||
|
if o == nil then return nil end
|
||||||
|
if seen[o] then return seen[o] end
|
||||||
|
|
||||||
|
local no
|
||||||
|
if type(o) == 'table' then
|
||||||
|
no = {}
|
||||||
|
seen[o] = no
|
||||||
|
|
||||||
|
for k, v in next, o, nil do
|
||||||
|
no[deepcopy(k, seen)] = deepcopy(v, seen)
|
||||||
|
end
|
||||||
|
setmetatable(no, deepcopy(getmetatable(o), seen))
|
||||||
|
else -- number, string, boolean, etc
|
||||||
|
no = o
|
||||||
|
end
|
||||||
|
return no
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function M.merge_schemas(schema1, schema2)
|
||||||
|
local merged = {}
|
||||||
|
|
||||||
|
merged.type = merge_types(schema1.type, schema2.type)
|
||||||
|
|
||||||
|
function add_property(pkey, pdef)
|
||||||
|
merged.properties = merged.properties or {}
|
||||||
|
merged.properties[pkey] = pdef
|
||||||
|
end
|
||||||
|
|
||||||
|
if not merged.type or merged.type == 'object' then
|
||||||
|
-- generate merged.properties
|
||||||
|
local properties1 = schema1.properties or {}
|
||||||
|
local properties2 = schema2.properties or {}
|
||||||
|
|
||||||
|
for _, pkey in pairs(merged_keys(properties1, properties2)) do
|
||||||
|
local pdef1 = properties1[pkey]
|
||||||
|
local pdef2 = properties2[pkey]
|
||||||
|
|
||||||
|
if pdef1 and pdef2 then
|
||||||
|
add_property(pkey, merge_schemas(pdef1, pdef2))
|
||||||
|
elseif pdef1 then
|
||||||
|
add_property(pkey, deepcopy(pdef1))
|
||||||
|
elseif pdef2 then
|
||||||
|
add_property(pkey, deepcopy(pdef2))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- generate merged.additionalProperties
|
||||||
|
if schema1.additionalProperties and schema2.additionalProperties then
|
||||||
|
merged.additionalProperties = merge_schemas(
|
||||||
|
schema1.additionalProperties, schema2.additionalProperties)
|
||||||
|
else
|
||||||
|
merged.additionalProperties = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- generate merged.required
|
||||||
|
merged.required = merged_values(schema1, schema2)
|
||||||
|
if #merged.required == 0 then
|
||||||
|
merged.required = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- TODO: implement array
|
||||||
|
|
||||||
|
-- generate merged.default
|
||||||
|
merged.default = schema2.default or schema1.default
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
@ -0,0 +1,34 @@
|
|||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.schema(site, platform)
|
||||||
|
return {
|
||||||
|
type = 'object',
|
||||||
|
properties = {
|
||||||
|
wizard = {
|
||||||
|
type = 'object',
|
||||||
|
properties = {
|
||||||
|
contact = {
|
||||||
|
type = 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.save(config, uci)
|
||||||
|
local owner = uci:get_first("gluon-node-info", "owner")
|
||||||
|
|
||||||
|
uci:set("gluon-node-info", owner, "contact", config.wizard.contact)
|
||||||
|
uci:save("gluon-node-info")
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.get(uci, config)
|
||||||
|
local owner = uci:get_first("gluon-node-info", "owner")
|
||||||
|
|
||||||
|
config.wizard = config.wizard or {}
|
||||||
|
config.wizard.contact = uci:get("gluon-node-info", owner, "contact")
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
8
package/gluon-config-api/luasrc/lib/gluon/config-api/www/cgi-bin/api
Executable file
8
package/gluon-config-api/luasrc/lib/gluon/config-api/www/cgi-bin/api
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
|
||||||
|
require 'gluon.web.cgi' {
|
||||||
|
base_path = '/lib/gluon/config-api',
|
||||||
|
|
||||||
|
layout_package = 'gluon-config-api',
|
||||||
|
layout_template = 'theme/layout', -- only used for error pages
|
||||||
|
}
|
36
package/gluon-config-api/luasrc/lib/gluon/upgrade/500-config-api
Executable file
36
package/gluon-config-api/luasrc/lib/gluon/upgrade/500-config-api
Executable file
@ -0,0 +1,36 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
|
||||||
|
local uci = require('simple-uci').cursor()
|
||||||
|
local site = require 'gluon.site'
|
||||||
|
|
||||||
|
local function get_mem_total()
|
||||||
|
for line in io.lines('/proc/meminfo') do
|
||||||
|
local match = line:match('^MemTotal:%s+(%d+)')
|
||||||
|
if match then
|
||||||
|
return tonumber(match)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local max_requests = 32
|
||||||
|
if get_mem_total() < 48*1024 then
|
||||||
|
max_requests = 16
|
||||||
|
end
|
||||||
|
|
||||||
|
uci:section('uhttpd', 'uhttpd', 'config_api', {
|
||||||
|
listen_http = { '0.0.0.0:83', '[::]:83' },
|
||||||
|
listen_https = {},
|
||||||
|
|
||||||
|
home = '/lib/gluon/config-api/www',
|
||||||
|
max_requests = max_requests,
|
||||||
|
max_connections = 100,
|
||||||
|
redirect_https = true,
|
||||||
|
rfc1918_filter = true,
|
||||||
|
cgi_prefix = '/cgi-bin',
|
||||||
|
script_timeout = 60,
|
||||||
|
network_timeout = 30,
|
||||||
|
http_keepalive = 20,
|
||||||
|
tcp_keepalive = true,
|
||||||
|
})
|
||||||
|
uci:save('uhttpd')
|
||||||
|
uci:save('firewall')
|
65
package/libucl/Makefile
Normal file
65
package/libucl/Makefile
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2007-2014 OpenWrt.org
|
||||||
|
#
|
||||||
|
# This is free software, licensed under the GNU General Public License v2.
|
||||||
|
# See /LICENSE for more information.
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=libucl
|
||||||
|
PKG_VERSION:=0.8.1
|
||||||
|
PKG_RELEASE:=1
|
||||||
|
|
||||||
|
PKG_SOURCE_PROTO:=git
|
||||||
|
PKG_SOURCE_URL:=https://github.com/vstakhov/libucl
|
||||||
|
PKG_SOURCE_VERSION:=e6c5d8079b95796099693b0889f07a036f78ad77
|
||||||
|
PKG_MAINTAINER:=Leonardo Mörlein <me@irrelefant.net>
|
||||||
|
|
||||||
|
PKG_LICENSE:=BSD-2-Clause
|
||||||
|
|
||||||
|
PKG_FIXUP:=autoreconf
|
||||||
|
# PKG_FIXUP:=patch-libtool
|
||||||
|
# PKG_FIXUP:=gettext-version
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
include $(INCLUDE_DIR)/autotools.mk
|
||||||
|
|
||||||
|
CONFIGURE_ARGS += \
|
||||||
|
--enable-lua
|
||||||
|
|
||||||
|
define Package/libucl
|
||||||
|
SECTION:=libs
|
||||||
|
CATEGORY:=Libraries
|
||||||
|
TITLE:=Config Parsing
|
||||||
|
DEPENDS:=+liblua
|
||||||
|
URL:=https://github.com/vstakhov/libucl
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/libucl/description
|
||||||
|
Universal configuration library parser.
|
||||||
|
endef
|
||||||
|
|
||||||
|
# MAKE_FLAGS += \
|
||||||
|
# CFLAGS="$(TARGET_CFLAGS) $(FPIC)"
|
||||||
|
|
||||||
|
define Package/libucl/install
|
||||||
|
$(INSTALL_DIR) $(1)/usr/lib
|
||||||
|
$(CP) $(PKG_BUILD_DIR)/src/.libs/libucl.so $(1)/usr/lib/
|
||||||
|
$(CP) $(PKG_BUILD_DIR)/src/.libs/libucl.so.5 $(1)/usr/lib/
|
||||||
|
$(CP) $(PKG_BUILD_DIR)/src/.libs/libucl.so.5.1.0 $(1)/usr/lib/
|
||||||
|
$(INSTALL_DIR) $(1)/usr/lib/lua
|
||||||
|
$(CP) $(PKG_BUILD_DIR)/lua/.libs/ucl.so $(1)/usr/lib/lua/ucl.so
|
||||||
|
endef
|
||||||
|
|
||||||
|
|
||||||
|
# define Build/InstallDev
|
||||||
|
# $(INSTALL_DIR) $(1)/usr/include
|
||||||
|
# $(CP) $(PKG_BUILD_DIR)/argp.h \
|
||||||
|
# $(1)/usr/include/
|
||||||
|
# $(INSTALL_DIR) $(1)/usr/lib
|
||||||
|
# $(CP) $(PKG_BUILD_DIR)/libargp.a \
|
||||||
|
# $(1)/usr/lib/
|
||||||
|
# endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,libucl))
|
Loading…
Reference in New Issue
Block a user