implement display and serving of archived artifacts

pull/5/head
Oliver Giles 9 years ago
parent dbc75000a5
commit a729a6782e

@ -15,3 +15,13 @@
### will not be deleted after the run has completed
###
#LAMINAR_KEEP_WORKDIR=1
###
### LAMINAR_ARCHIVE_URL
###
### Base url used to request artifacts. Laminar can serve build
### artifacts (and it will if you leave this unset), but it
### uses a very naive and inefficient method. Best to let a real
### webserver handle serving those requests.
###
#LAMINAR_ARCHIVE_URL=http://backbone.example.com/ci/archive

@ -107,6 +107,11 @@ struct LaminarInterface {
// arbitrary parameters on a run (usually itself) to be available in
// the environment of subsequent scripts.
virtual bool setParam(std::string job, int buildNum, std::string param, std::string value) = 0;
// Fetches the content of an artifact given its filename relative to
// $LAMINAR_HOME/archive. This shouldn't be used, because the sysadmin
// should have configured a real webserver to serve these things.
virtual bool getArtefact(std::string path, std::string& result) = 0;
};
#endif // INTERFACE_H

@ -21,6 +21,7 @@
#include "conf.h"
#include <sys/wait.h>
#include <fstream>
#include <kj/debug.h>
#include <boost/filesystem.hpp>
@ -56,12 +57,15 @@ namespace {
// Default values when none were supplied in $LAMINAR_CONF_FILE (/etc/laminar.conf)
constexpr const char* INTADDR_RPC_DEFAULT = "unix:\0laminar";
constexpr const char* INTADDR_HTTP_DEFAULT = "*:8080";
constexpr const char* BASE_CFG_DIR = "/home/og/dev/laminar/cfg";
constexpr const char* ARCHIVE_URL_DEFAULT = "/archive";
}
typedef std::string str;
Laminar::Laminar() {
archiveUrl = ARCHIVE_URL_DEFAULT;
if(char* envArchive = getenv("LAMINAR_ARCHIVE_URL"))
archiveUrl = envArchive;
eraseWorkdir = true;
homeDir = getenv("LAMINAR_HOME") ?: "/var/lib/laminar";
@ -125,6 +129,20 @@ void Laminar::sendStatus(LaminarClient* client) {
j.set("result", to_string(RunState(result)));
j.set("reason", reason);
});
j.startArray("artifacts");
fs::path dir(fs::path(homeDir)/"archive"/client->scope.job/std::to_string(client->scope.num));
fs::recursive_directory_iterator rdt(dir);
int prefixLen = (fs::path(homeDir)/"archive").string().length();
int scopeLen = dir.string().length();
for(fs::directory_entry e : rdt) {
if(!fs::is_regular_file(e))
continue;
j.StartObject();
j.set("url", archiveUrl + e.path().string().substr(prefixLen));
j.set("filename", e.path().string().substr(scopeLen+1));
j.EndObject();
}
j.EndArray();
j.EndObject();
client->sendMessage(j.str());
} else if(client->scope.type == MonitorScope::JOB) {
@ -556,3 +574,26 @@ void Laminar::runFinished(const Run * r) {
// will delete the job
activeJobs.get<2>().erase(r);
}
bool Laminar::getArtefact(std::string path, std::string& result) {
if(archiveUrl != ARCHIVE_URL_DEFAULT) {
// we shouldn't have got here. Probably an invalid link.
return false;
}
// Reads in the whole file into the given string reference.
// This is a terrible way to serve files (especially large ones).
fs::path file(fs::path(homeDir)/"archive"/path);
// no support for browsing archived directories
if(fs::is_directory(file))
return false;
std::ifstream fstr(file.string());
fstr.seekg(0, std::ios::end);
size_t sz = fstr.tellg();
if(fstr.rdstate() == 0) {
result.resize(sz);
fstr.seekg(0);
fstr.read(&result[0], sz);
return true;
}
return false;
}

@ -53,6 +53,7 @@ public:
void deregisterClient(LaminarClient* client) override;
void sendStatus(LaminarClient* client) override;
bool setParam(std::string job, int buildNum, std::string param, std::string value) override;
bool getArtefact(std::string path, std::string& result) override;
private:
bool loadConfiguration();
@ -85,6 +86,7 @@ private:
std::string homeDir;
std::set<LaminarClient*> clients;
bool eraseWorkdir;
std::string archiveUrl;
};
#endif // _LAMINAR_LAMINAR_H_

@ -7,6 +7,7 @@
<dt></dt><dd>&nbsp;</dd>
<dt>Reason</dt><dd>{{job.reason}}</dd>
<dt>Started</dt><dd>{{job.when}}</dd>
<dt>Artifacts</dt><dd><ul><li ng-repeat="art in job.artifacts"><a href="{{art.url}}" target="_self">{{art.filename}}</a></li></ul></dd>
</dl>
</div>
</div>

@ -168,7 +168,17 @@ public:
wss.set_http_handler([this](websocketpp::connection_hdl hdl){
websocket::connection_ptr c = wss.get_con_from_hdl(hdl);
const char* start, *end;
if(resources.handleRequest(c->get_resource(), &start, &end)) {
std::string resource = c->get_resource();
if(resource.compare(0, strlen("/archive/"), "/archive/") == 0) {
std::string file(resource.substr(strlen("/archive/")));
std::string content;
if(laminar.getArtefact(file, content)) {
c->set_status(websocketpp::http::status_code::ok);
c->set_body(content);
} else {
c->set_status(websocketpp::http::status_code::not_found);
}
} else if(resources.handleRequest(resource, &start, &end)) {
c->set_status(websocketpp::http::status_code::ok);
c->append_header("Content-Encoding", "gzip");
c->set_body(std::string(start, end));

Loading…
Cancel
Save