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