check_site.lua 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. local cjson = require 'cjson'
  2. local function exit_error(src, ...)
  3. io.stderr:write(string.format('*** %s error: %s\n', src, string.format(...)))
  4. os.exit(1)
  5. end
  6. local has_domains = (os.execute('ls -d "$IPKG_INSTROOT"/lib/gluon/domains/ >/dev/null 2>&1') == 0)
  7. local function load_json(filename)
  8. local f = assert(io.open(filename))
  9. local json = cjson.decode(f:read('*a'))
  10. f:close()
  11. return json
  12. end
  13. local function get_domains()
  14. local domains = {}
  15. local dirs = io.popen("find \"$IPKG_INSTROOT\"/lib/gluon/domains/ -name '*.json'")
  16. for filename in dirs:lines() do
  17. local name = string.match(filename, '([^/]+).json$')
  18. domains[name] = load_json(filename)
  19. end
  20. dirs:close()
  21. if not next(domains) then
  22. exit_error('site', 'no domain configurations found')
  23. end
  24. return domains
  25. end
  26. local site, domain_code, domain, conf
  27. local function merge(a, b)
  28. local function is_array(t)
  29. local n = 0
  30. for k, v in pairs(t) do
  31. n = n + 1
  32. end
  33. return n == #t
  34. end
  35. if not b then return a end
  36. if type(a) ~= type(b) then return b end
  37. if type(b) ~= 'table' then return b end
  38. if not next(b) then return a end
  39. if is_array(a) ~= is_array(b) then return b end
  40. local m = {}
  41. for k, v in pairs(a) do
  42. m[k] = v
  43. end
  44. for k, v in pairs(b) do
  45. m[k] = merge(m[k], v)
  46. end
  47. return m
  48. end
  49. local function path_to_string(path)
  50. return table.concat(path, '/')
  51. end
  52. local function array_to_string(array)
  53. return '[' .. table.concat(array, ', ') .. ']'
  54. end
  55. local loadpath
  56. local function site_src()
  57. return 'site.conf'
  58. end
  59. local function domain_src()
  60. return 'domains/' .. domain_code .. '.conf'
  61. end
  62. local function var_error(path, val, msg)
  63. if type(val) == 'string' then
  64. val = string.format('%q', val)
  65. end
  66. local src
  67. if has_domains then
  68. if loadpath(nil, domain, unpack(path)) ~= nil then
  69. src = domain_src()
  70. elseif loadpath(nil, site, unpack(path)) ~= nil then
  71. src = site_src()
  72. else
  73. src = site_src() .. ' / ' .. domain_src()
  74. end
  75. else
  76. src = site_src()
  77. end
  78. exit_error(src, 'expected %s to %s, but it is %s', path_to_string(path), msg, tostring(val))
  79. end
  80. function in_site(path)
  81. if has_domains and loadpath(nil, domain, unpack(path)) ~= nil then
  82. exit_error(domain_src(), '%s is allowed in site configuration only', path_to_string(path))
  83. end
  84. return path
  85. end
  86. function in_domain(path)
  87. if has_domains and loadpath(nil, site, unpack(path)) ~= nil then
  88. exit_error(site_src(), '%s is allowed in domain configuration only', path_to_string(path))
  89. end
  90. return path
  91. end
  92. function this_domain()
  93. return domain_code
  94. end
  95. function extend(path, c)
  96. if not path then return nil end
  97. local p = {unpack(path)}
  98. for _, e in ipairs(c) do
  99. p[#p+1] = e
  100. end
  101. return p
  102. end
  103. function loadpath(path, base, c, ...)
  104. if not c or base == nil then
  105. return base
  106. end
  107. if type(base) ~= 'table' then
  108. if path then
  109. var_error(path, base, 'be a table')
  110. else
  111. return nil
  112. end
  113. end
  114. return loadpath(extend(path, {c}), base[c], ...)
  115. end
  116. local function loadvar(path)
  117. return loadpath({}, conf, unpack(path))
  118. end
  119. local function check_type(t)
  120. return function(val)
  121. return type(val) == t
  122. end
  123. end
  124. local function check_one_of(array)
  125. return function(val)
  126. for _, v in ipairs(array) do
  127. if v == val then
  128. return true
  129. end
  130. end
  131. return false
  132. end
  133. end
  134. function need(path, check, required, msg)
  135. local val = loadvar(path)
  136. if required == false and val == nil then
  137. return nil
  138. end
  139. if not check(val) then
  140. var_error(path, val, msg)
  141. end
  142. return val
  143. end
  144. local function need_type(path, type, required, msg)
  145. return need(path, check_type(type), required, msg)
  146. end
  147. function need_alphanumeric_key(path)
  148. local val = path[#path]
  149. -- We don't use character classes like %w here to be independent of the locale
  150. if not val:match('^[0-9a-zA-Z_]+$') then
  151. var_error(path, val, 'have a key using only alphanumeric characters and underscores')
  152. end
  153. end
  154. function need_string(path, required)
  155. return need_type(path, 'string', required, 'be a string')
  156. end
  157. function need_string_match(path, pat, required)
  158. local val = need_string(path, required)
  159. if not val then
  160. return nil
  161. end
  162. if not val:match(pat) then
  163. var_error(path, val, "match pattern '" .. pat .. "'")
  164. end
  165. return val
  166. end
  167. function need_number(path, required)
  168. return need_type(path, 'number', required, 'be a number')
  169. end
  170. function need_boolean(path, required)
  171. return need_type(path, 'boolean', required, 'be a boolean')
  172. end
  173. function need_array(path, subcheck, required)
  174. local val = need_type(path, 'table', required, 'be an array')
  175. if not val then
  176. return nil
  177. end
  178. if subcheck then
  179. for i = 1, #val do
  180. subcheck(extend(path, {i}))
  181. end
  182. end
  183. return val
  184. end
  185. function need_table(path, subcheck, required)
  186. local val = need_type(path, 'table', required, 'be a table')
  187. if not val then
  188. return nil
  189. end
  190. if subcheck then
  191. for k, _ in pairs(val) do
  192. subcheck(extend(path, {k}))
  193. end
  194. end
  195. return val
  196. end
  197. function need_value(path, value, required)
  198. return need(path, function(v)
  199. return v == value
  200. end, required, 'be ' .. tostring(value))
  201. end
  202. function need_one_of(path, array, required)
  203. return need(path, check_one_of(array), required, 'be one of the given array ' .. array_to_string(array))
  204. end
  205. function need_string_array(path, required)
  206. return need_array(path, need_string, required)
  207. end
  208. function need_string_array_match(path, pat, required)
  209. return need_array(path, function(e) need_string_match(e, pat) end, required)
  210. end
  211. function need_array_of(path, array, required)
  212. return need_array(path, function(e) need_one_of(e, array) end, required)
  213. end
  214. local check = assert(loadfile())
  215. site = load_json(os.getenv('IPKG_INSTROOT') .. '/lib/gluon/site.json')
  216. if has_domains then
  217. for k, v in pairs(get_domains()) do
  218. domain_code = k
  219. domain = v
  220. conf = merge(site, domain)
  221. check()
  222. end
  223. else
  224. conf = site
  225. check()
  226. end