summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTimo Teräs <timo.teras@iki.fi>2014-05-12 15:36:16 +0300
committerTimo Teräs <timo.teras@iki.fi>2014-05-12 15:38:15 +0300
commit2eb5dec338d1b1d183bf228aea632dc13049a70d (patch)
tree6cb120cecc811e8b8d4d780e2487c28b9018ddb0
parenta64571d4072a4af87ad8167a735ea75f37940d2e (diff)
downloadaports-2eb5dec338d1b1d183bf228aea632dc13049a70d.tar.bz2
aports-2eb5dec338d1b1d183bf228aea632dc13049a70d.tar.xz
testing/aaudit: improve email sending, basic cron job skeleton
-rw-r--r--testing/aaudit/APKBUILD34
-rwxr-xr-xtesting/aaudit/aaudit17
-rwxr-xr-xtesting/aaudit/aaudit-repo47
-rw-r--r--testing/aaudit/aaudit-server.json1
-rw-r--r--testing/aaudit/aaudit-server.lua177
-rw-r--r--testing/aaudit/aaudit.json6
6 files changed, 195 insertions, 87 deletions
diff --git a/testing/aaudit/APKBUILD b/testing/aaudit/APKBUILD
index 0dc7179e0..62edc714d 100644
--- a/testing/aaudit/APKBUILD
+++ b/testing/aaudit/APKBUILD
@@ -1,13 +1,13 @@
# Contributor: Timo Teräs <timo.teras@iki.fi>
# Maintainer: Timo Teräs <timo.teras@iki.fi>
pkgname=aaudit
-pkgver=0.5
+pkgver=0.6
pkgrel=0
pkgdesc="Alpine Auditor"
url="http://alpinelinux.org"
arch="noarch"
license="GPL"
-depends="lua5.2 lua5.2-posix lua5.2-cjson lua5.2-pc"
+depends="lua5.2 lua5.2-posix lua5.2-cjson lua5.2-pc lua5.2-socket"
makedepends=""
install=""
subpackages="$pkgname-server"
@@ -17,6 +17,7 @@ source="aaudit-common.lua
aaudit
aaudit-shell
aaudit-update-keys
+ aaudit-repo
aaudit.json
aaudit-server.json
"
@@ -44,28 +45,31 @@ server() {
"$subpkgdir"/usr/share/lua/$_luaver/aaudit
cp aaudit-server.json "$subpkgdir"/etc/aaudit
cp aaudit-server.lua "$subpkgdir"/usr/share/lua/$_luaver/aaudit/server.lua
- cp aaudit-update-keys "$subpkgdir"/usr/bin
+ cp aaudit-update-keys aaudit-repo "$subpkgdir"/usr/bin
cp aaudit-shell "$subpkgdir"/usr/libexec/aaudit
}
md5sums="b81b0707b297a69dacbc1606324de029 aaudit-common.lua
-2dfb114024f0f7aa46400a6c81d1d9cc aaudit-server.lua
-9b48cc51f9f16989af4290107b2b866d aaudit
+aaca139a4d476cadd1e7f8516caafec4 aaudit-server.lua
+028ecf2a733387560b15563de1777c68 aaudit
f5de73f12b6df1a751c89f19c92871be aaudit-shell
feef077f56f40002ca11846512d347af aaudit-update-keys
-e14ded329626ca1d6dd48e5bef0bc7e2 aaudit.json
-f2c15c547701ec71aa4d0c1d0f7b70b1 aaudit-server.json"
+9c88f34f561a2d0955b07de467b18a70 aaudit-repo
+8a80554c91d9fca8acb82f023de02f11 aaudit.json
+f0e97a7a07a472278298b5999a397f58 aaudit-server.json"
sha256sums="ee1998e730356c2de0ff9d5e27d9e0277e3c1f051777146b7c5b820437edfd7f aaudit-common.lua
-55f70bb0eb60b33580a6f68f57f0a8b52fef4f2bf4c4851af7a9b96c60f4841a aaudit-server.lua
-160a4e2893dfb264a7844d911d7c2781dda4624f2357f3e7950338f8d2ca9c2a aaudit
+fb4ae6432f0682fccd6926e0520037cf40bc41e8781c3dfe6e70101926c5588d aaudit-server.lua
+91b5247e856b6531796a0ac61c3c82a37880fbabc1afc9bf4793667f03fb3ea0 aaudit
659c755cfca95a76da78f4d28d0ab9a32d55bea0077be7420ceaf9d45c518354 aaudit-shell
660dcf86f02a9d0e3ff47cb359e0291a0921d03215e368552a2878d2d691a9cc aaudit-update-keys
-f61efebc04756c8bfb7cb955b7af5db6a3c5dabdd005f690db812c7e77567cf5 aaudit.json
-d477ca7714ff43818f8deef3f60d69d118ea899c57b6109cdf4c6c22009cc07c aaudit-server.json"
+83868f17e1162e2b621eb2115a36f989c300aeda7cadf82ec1c991ee19d25664 aaudit-repo
+ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356 aaudit.json
+ab276c9caeaa238c8999c79cd3b016f4ba149e6793475c5cde088597cedd849c aaudit-server.json"
sha512sums="aaa378fd710d17cb3663954648e97dd5128406cc6f37e9834075046aed1912dcc9e448b6c96502350b8d3496e00b7803cae671a4be2c12c584a84dc0b6e843e9 aaudit-common.lua
-72fb60c37a54b34feff48d1658ecc341937c9fb9148dafd71e19347c6e1cd2daf4f59dfb443490b14f82f543847a56b04c877eb545dc9ee6b4af310a94e1008d aaudit-server.lua
-1c9a78d1d2557353982ae5edbad7fefb2983ba06ff139352d2106dbba00f675cc50050ad5dd7a62153b8c8c8a6ec953872cb5c0289fa999656ade8f8004a3f5e aaudit
+a78e3600fbac793040f3ece9171a47e8e624d26860b1f68bfca8812a07592c7c8f70e2806184a2359f81f5fb43d67520eb2de3f3f2145804dd1a8ee1266e1a45 aaudit-server.lua
+e12b1623506382e04307f1c7fc361b544b4fbe992d41fcbaa7efd4c8568060fa7fad17e4db7a4ae96ebadc2b95f3c545809b948460a5446608bac6a35d3c35f3 aaudit
18499771d7d425f9305209c562eb8e62ef41910e88b08219baf010cdb472d49087080feb67384c4826c53bddcc0ce92c0c23c78df22dc40c64f1b17bf0ad05ec aaudit-shell
aec728a9a1e4c92baeb94a9d95e1785ea166652a157571fe2e848e71c1246635ecb99512e92435e1314c620b1fa8e4f37400350bed78bd375db4a63828c500f0 aaudit-update-keys
-e769f0f77fe54ba1ab35efc80cc6426e34a2ee1d053ac9e7cc5aa316cfcef0c9658d2f0e2c47f7ae282bb9cc07107065fcc13034b2f9125c182378b7c73b7d99 aaudit.json
-69f8da6b6057dfb5ab0f36f8b27db3a275cd24e4d45a0e95cf4a79d955c0ebfc88861a0a50d5b6da542587cbe8feca1842c966026f0bf445edea316202d49ee5 aaudit-server.json"
+7507dea2b8ec4054e507aa3b42818863b9737402f3a9f967e16a63dd3ea12385463c2bce178a819a5f1fef76e74887a136de665e0b54172f1c1ce5f61b73403f aaudit-repo
+ca4b6defb8adcc010050bc8b1bb8f8092c4928b8a0fba32146abcfb256e4d91672f88ca2cdf6210e754e5b8ac5e23fb023806ccd749ac8b701f79a691f03c87a aaudit.json
+52da35598b8638a34d5a6352b2ccfd046dc529e0e5e6b541d3111016cbe6b091ff3fb4175e98b4f39e226ca1e6c973b9aa9a7a74fcb49b41862bbe64979d9186 aaudit-server.json"
diff --git a/testing/aaudit/aaudit b/testing/aaudit/aaudit
index f17c6cc6a..44652a1de 100755
--- a/testing/aaudit/aaudit
+++ b/testing/aaudit/aaudit
@@ -9,17 +9,15 @@ local function usage()
print([[
Usage: aaudit [create|commit] [OPTIONS...]
-Valid options for create:
+Options for create:
-s SERV Use server SERV
-d DESC Description for repository (default: hostname)
-t ADDR Specify ADDR as target device (default: local source IP)
- -m MSG Specify message for the initial commit
-g GRP Add in group GRP (can be specified multiple times)
-Valid options for commit:
+Options for create and commit:
-m MSG Specify message for the commit
- -r RT Related to ticket RT
- -c RT Closes ticket RT
+ -L Local change (use local 'contact' as change author)
]])
os.exit(1)
end
@@ -28,7 +26,7 @@ local verbose = false
local conf = aac.readconfig() or {}
local req = {}
-for ret, optval in posix.getopt(arg, 'vs:d:t:m:g:r:c:') do
+for ret, optval in posix.getopt(arg, 'vs:d:t:m:Lg:') do
if ret == 'v' then
verbose = true
elseif ret == 's' then
@@ -39,14 +37,11 @@ for ret, optval in posix.getopt(arg, 'vs:d:t:m:g:r:c:') do
conf.target_address = optval
elseif ret == 'm' then
req.message = optval
+ elseif ret == 'L' then
+ req.local_change = true
elseif ret == 'g' then
req.groups = req.groups or {}
table.insert(req.groups, optval)
- elseif ret == 'r' then
- req.ticket = optval
- elseif ret == 'c' then
- req.ticket = optval
- req.ticket_action = "close"
else
usage()
end
diff --git a/testing/aaudit/aaudit-repo b/testing/aaudit/aaudit-repo
new file mode 100755
index 000000000..dbacd976b
--- /dev/null
+++ b/testing/aaudit/aaudit-repo
@@ -0,0 +1,47 @@
+#!/usr/bin/lua5.2
+
+local posix = require 'posix'
+local aac = require 'aaudit.common'
+local aas = require 'aaudit.server'
+
+local pullafter = aas.serverconfig["pull-after"] or 24*60*60
+local warnafter = aas.serverconfig["warn-after"] or 4*24*60*60
+
+local function dorepo(repodir)
+ -- Check if it's time to update
+ local repoconf = aas.loadrepoconfig(repodir)
+ local stampfile = ("%s/lastcheck"):format(repodir)
+ local mtime = posix.stat(stampfile, "mtime") or 0
+
+ if os.time() > mtime + pullafter then
+ -- Pull for changes
+ local req = {
+ command = "commit",
+ target_address = repoconf.address,
+ message = "Unexpected configuration change",
+ local_change = true,
+ }
+ local ret, msg = aas.handle(req)
+ print(("Updating repository %s -> %s: %s"):format(repodir, repoconf.address, msg))
+ mtime = posix.stat(stampfile, "mtime") or 0
+ end
+
+ return mtime, repoconf.address
+end
+
+local home = os.getenv("HOME")
+local outdated = {"List of unreachable monitored hosts:"}
+for _, repodir in ipairs(posix.glob(("%s/*.git"):format(home))) do
+ local mtime, address = dorepo(repodir)
+ if os.time() > mtime + warnafter then
+ table.insert(outdated, address)
+ end
+end
+
+if #outdated > 1 and aas.serverconfig["notify-unreachables"] then
+ aas.sendemail {
+ to = aas.serverconfig["notify-unreachables"],
+ subject = "aaudit report of unreachable hosts",
+ message = table.concat(outdated, "\n"),
+ }
+end
diff --git a/testing/aaudit/aaudit-server.json b/testing/aaudit/aaudit-server.json
index 269e61716..d00c0be81 100644
--- a/testing/aaudit/aaudit-server.json
+++ b/testing/aaudit/aaudit-server.json
@@ -1,5 +1,6 @@
{
"smtp_server": "localhost",
+ "rtqueue": "rtqueue",
"identities": {
"_default": "Alpine Auditor <auditor@alpine.local>"
},
diff --git a/testing/aaudit/aaudit-server.lua b/testing/aaudit/aaudit-server.lua
index e6ed3a65e..89f2fc021 100644
--- a/testing/aaudit/aaudit-server.lua
+++ b/testing/aaudit/aaudit-server.lua
@@ -4,9 +4,12 @@ local posix = require 'posix'
local json = require 'cjson'
local zlib = require 'zlib'
local aac = require 'aaudit.common'
+local smtp = require 'socket.smtp'
local HOME = os.getenv("HOME")
+M.serverconfig = aac.readconfig(("%s/aaudit-server.json"):format(HOME)) or {}
+
local function merge_bool(a, b) return a or b end
local function merge_array(a, b) if b then for i=1,#b do a[#a+1] = b[i] end end return a end
@@ -70,6 +73,85 @@ local function read_header_block(block)
return header
end
+local function rfc822_address(id)
+ local identities = M.serverconfig.identities
+ if id == nil then id = "_default" end
+ if identities and identities[id] then id = identities[id] end
+ local name, email = id:match("^(.-) *(<.*>)$")
+ if not email then return ("<%s>"):format(id) end
+ return ("%s %s"):format(name, email)
+end
+
+local function rfc822_email(rfc822)
+ return rfc822:match("(<.*>)$")
+
+end
+
+function M.sendemail(mail)
+ local to = {}
+ local m = {
+ headers = {
+ ["Content-Type"] = 'text/plain; charset=utf8',
+ ["X-RT-Command"] = mail.rtheader,
+ from = rfc822_address(mail.from),
+ subject = mail.subject,
+ },
+ body = mail.message,
+ }
+ local rcpt = {}
+ for _, addr in ipairs(mail.to) do
+ local rfc822 = rfc822_address(addr)
+ table.insert(to, rfc822)
+ table.insert(rcpt, rfc822_email(rfc822))
+ end
+ m.headers.to = table.concat(to, ", ")
+ return smtp.send{
+ from = rfc822_email(m.headers.from),
+ rcpt = rcpt,
+ source = smtp.message(m)
+ }
+end
+
+local rt_keywords = {
+ fix = true,
+ fixes = true,
+ close = true,
+ closes = true,
+ ref = false,
+ refs = false,
+ rt = false,
+}
+
+local function sendcommitdiff(body, req, R, G)
+ if not body then return end
+ if not G.notify_emails then return end
+
+ local mail = {
+ from = req.committer,
+ to = G.notify_emails,
+ subject = ("config change - %s (%s)"):format(R.description, R.address),
+ message = table.concat(body, '\n')
+ }
+
+ -- Set Request Tracker headers if relevant
+ local rtqueue = M.serverconfig.rtqueue
+ if rtqueue then
+ for k,no in req.message:gmatch("(%a+) #(%d+)") do
+ local action = rt_keywords[k]
+ if action ~= nil then
+ mail.subject = ("[%s #%s] %s"):format(rtqueue, no, mail.subject)
+ if action == true then
+ mail.rtheader = "Status: resolved"
+ end
+ break
+ end
+ end
+ end
+
+ -- Send email
+ return M.sendemail(mail)
+end
+
local function import_tar(TAR, GIT, req, G)
local branch_ref = "refs/heads/import"
local from_ref = "refs/heads/master"
@@ -107,6 +189,17 @@ local function import_tar(TAR, GIT, req, G)
end
end
+ if header.name == "etc/aaudit/aaudit.json" then
+ local success, res = pcall(json.decode, file_data)
+ if success and res.contact then
+ local contact = res.contact
+ G.notify_emails = merge_array(G.notify_emails, {contact})
+ if req.local_change then
+ req.author = rfc822_address(res.contact)
+ end
+ end
+ end
+
if header.typeflag:match("^[0-46]$") and
not match_file(header.name, G.no_track) then
GIT:write('blob\n', 'mark :', nextmark, '\n')
@@ -140,9 +233,9 @@ data <<END_OF_COMMIT_MESSAGE
END_OF_COMMIT_MESSAGE
]]):format(branch_ref,
- req.author.rfc822, author_time,
- req.author.rfc822, os.time(),
- req.message or "Changes"))
+ req.author, author_time,
+ req.committer, os.time(),
+ req.message))
if not req.initial then GIT:write(("from %s^0\n"):format(from_ref)) end
GIT:write("deleteall\n")
@@ -191,70 +284,31 @@ local function generate_diff(repodir, commit, G)
return has_changes, text
end
-local function resolve_email(identities, id)
- if identities and identities[id] then id = identities[id] end
- local name, email = id:match("^(.-) *<(.*)>$")
- if email then return {name=name, email=email, rfc822=("%s <%s>"):format(name, email) } end
- return {name="", email=name, rfc822=("<%s>"):format(name)}
-end
-
-local function send_email(body, req, S, R, G)
- if not body then return end
- if not G.notify_emails then return end
-
- local to_rfc822 = {}
- local to_email = {}
- for _,r in ipairs(G.notify_emails) do
- local id = resolve_email(S.identities, r)
- if not to_email[id.email] then
- to_email[id.email] = true
- table.insert(to_rfc822, id.rfc822)
- table.insert(to_email, id.email)
- end
- end
- to_rfc822 = table.concat(to_rfc822, ", ")
- to_email = table.concat(to_email, " ")
-
- -- Add busybox sendmail smtp server option
- local options=""
- if S.smtp_server then options = ('-S "%s"'):format(S.smtp_server) end
-
- local EMAIL = io.popen(('sendmail -f "%s" %s %s'):format(req.author.email, options, to_email), "w")
- EMAIL:write(([[
-From: %s
-To: %s
-Subject: apkovl changed - %s (%s)
-Date: %s
-
-]]):format(req.author.rfc822, to_rfc822, R.description, R.address, os.date("%a, %d %b %Y %H:%M:%S")))
-
- for _, l in ipairs(body) do EMAIL:write(l,'\n') end
- EMAIL:close()
-
- return to_email
+function M.loadrepoconfig(repohome)
+ return aac.readconfig(("%s/aaudit-repo.json"):format(repohome))
end
local function load_repo_configs(repohome)
- local S = aac.readconfig(("%s/aaudit-server.json"):format(HOME))
- local R = aac.readconfig(("%s/aaudit-repo.json"):format(repohome))
+ local R = M.loadrepoconfig(repohome)
-- merge global and per-repository group configs
- local G = (S.groups or {}).all or {}
+ local G = (M.serverconfig.groups or {}).all or {}
for _, name in pairs(R.groups or {}) do
- local g = S.groups[name] or {}
+ local g = M.serverconfig.groups[name] or {}
G.notify_emails = merge_array(G.notify_emails, g.notify_emails)
G.track_filemode = merge_bool(G.track_filemode, g.track_filemode)
G.no_track = merge_array(G.no_track, g.no_track)
G.no_notify = merge_array(G.no_notify, g.no_notify)
G.no_diff = merge_array(G.no_diff, g.no_diff)
end
- return S, R, G
+ return R, G
end
function M.repo_update(req,clientstream)
local repodir = req.repositorydir
- local S, R, G = load_repo_configs(repodir)
+ local R, G = load_repo_configs(repodir)
- req.author = resolve_email(S.identities, req.identity)
+ req.committer = rfc822_address(req.identity)
+ req.author = req.committer
local TAR
if req.apkovl_follows then
@@ -269,16 +323,26 @@ function M.repo_update(req,clientstream)
TAR:close()
if not rc then return rc, err end
+ local stampfile = ("%s/lastcheck"):format(repodir)
+ if posix.utime(stampfile) ~= 0 then
+ posix.close(posix.open(stampfile, posix.O_CREAT, "0644"))
+ end
+
local has_changes, email_body = generate_diff(repodir, "import", G)
if has_changes then
+ if not req.initial then
+ local res, err = sendcommitdiff(email_body, req, R, G)
+ if not res then
+ os.execute(("git --git-dir='%s' branch --quiet -D import;"..
+ "git --git-dir='%s' gc --quiet --prune=now")
+ :format(repodir, repodir))
+ return false, err
+ end
+ end
os.execute(("git --git-dir='%s' branch --quiet --force master import;"..
"git --git-dir='%s' branch --quiet -D import")
:format(repodir, repodir))
- local to = nil
- if not req.initial then
- to = send_email(email_body, req, S, R, G)
- end
- return true, "Committed", {notified=to}
+ return true, "Committed"
end
os.execute(("git --git-dir='%s' branch --quiet -D import;"..
@@ -322,6 +386,7 @@ function M.handle(req,clientstream)
req.command = "commit"
end
if req.command == "commit" then
+ req.message = req.message or "Configuration change"
if not posix.access(req.repositorydir, "rwx") then
return false, "No such repository"
end
diff --git a/testing/aaudit/aaudit.json b/testing/aaudit/aaudit.json
index 958d60fbc..0967ef424 100644
--- a/testing/aaudit/aaudit.json
+++ b/testing/aaudit/aaudit.json
@@ -1,5 +1 @@
-{
- "user": "aaudit",
- "server": "aaudit.alpine.local",
- "rtqueue": "alpine.org"
-}
+{}