mirror of
https://github.com/ohwgiles/laminar.git
synced 2024-10-27 20:34:20 +00:00
implement display and serving of archived artifacts
This commit is contained in:
parent
dbc75000a5
commit
a729a6782e
10
laminar.conf
10
laminar.conf
@ -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> </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…
Reference in New Issue
Block a user