diff options
author | Timo Teräs <timo.teras@iki.fi> | 2014-05-12 15:36:16 +0300 |
---|---|---|
committer | Timo Teräs <timo.teras@iki.fi> | 2014-05-12 15:38:15 +0300 |
commit | 2eb5dec338d1b1d183bf228aea632dc13049a70d (patch) | |
tree | 6cb120cecc811e8b8d4d780e2487c28b9018ddb0 | |
parent | a64571d4072a4af87ad8167a735ea75f37940d2e (diff) | |
download | aports-2eb5dec338d1b1d183bf228aea632dc13049a70d.tar.bz2 aports-2eb5dec338d1b1d183bf228aea632dc13049a70d.tar.xz |
testing/aaudit: improve email sending, basic cron job skeleton
-rw-r--r-- | testing/aaudit/APKBUILD | 34 | ||||
-rwxr-xr-x | testing/aaudit/aaudit | 17 | ||||
-rwxr-xr-x | testing/aaudit/aaudit-repo | 47 | ||||
-rw-r--r-- | testing/aaudit/aaudit-server.json | 1 | ||||
-rw-r--r-- | testing/aaudit/aaudit-server.lua | 177 | ||||
-rw-r--r-- | testing/aaudit/aaudit.json | 6 |
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" -} +{} |