mirror of
https://github.com/ohwgiles/laminar.git
synced 2024-10-27 20:34:20 +00:00
resolves #67: laminarc list jobs
Implements the following laminarc commands: - show-jobs - show-running - show-queued - abort
This commit is contained in:
parent
21284731a3
commit
7cee824cee
@ -142,6 +142,14 @@ laminarc queue test-host test-target
|
||||
|
||||
This is against the design principles of Laminar and was deliberately excluded. Laminar's web UI is strictly read-only, making it simple to deploy in mixed-permission or public environments without an authentication layer. Furthermore, Laminar tries to encourage ideal continuous integration, where manual triggering is an anti-pattern. Want to make a release? Push a git tag and implement a post-receive hook. Want to re-run a build due to sporadic failure/flaky tests? Fix the tests locally and push a patch. Experience shows that a manual trigger such as a "Build Now" button is often used as a crutch to avoid doing the correct thing, negatively impacting traceability and quality.
|
||||
|
||||
## Listing jobs from the command line
|
||||
|
||||
`laminarc` may be used to inspect the server state:
|
||||
|
||||
- `laminarc show-jobs`: Lists all files matching `/var/lib/laminar/cfg/jobs/*.run` on the server side.
|
||||
- `laminarc show-running`: Lists all currently running jobs and their run numbers.
|
||||
- `laminarc show-queued`: Lists all jobs waiting in the queue.
|
||||
|
||||
## Triggering a job at a certain time
|
||||
|
||||
This is what `cron` is for. To trigger a build of `hello` every day at 0300, add
|
||||
@ -433,7 +441,9 @@ make -C src
|
||||
|
||||
---
|
||||
|
||||
# Abort on timeout
|
||||
# Aborting running jobs
|
||||
|
||||
## After a timeout
|
||||
|
||||
To configure a maximum execution time in seconds for a job, add a line to `/var/lib/laminar/cfg/jobs/JOBNAME.conf`:
|
||||
|
||||
@ -441,6 +451,10 @@ To configure a maximum execution time in seconds for a job, add a line to `/var/
|
||||
TIMEOUT=120
|
||||
```
|
||||
|
||||
## Manually
|
||||
|
||||
`laminarc abort $JOBNAME $NUMBER`
|
||||
|
||||
---
|
||||
|
||||
# Nodes and Tags
|
||||
@ -607,5 +621,9 @@ Finally, variables supplied on the command-line call to `laminarc queue`, `lamin
|
||||
- `start [JOB [PARAMS...]]...` starts one or more jobs with optional parameters, returning when the jobs begin execution.
|
||||
- `run [JOB [PARAMS...]]...` triggers one or more jobs with optional parameters and waits for the completion of all jobs. Returns a non-zero error code if any job failed.
|
||||
- `set [VARIABLE=VALUE]...` sets one or more variables to be exported in subsequent scripts for the run identified by the `$JOB` and `$RUN` environment variables
|
||||
- `show-jobs` shows the known jobs on the server (`$LAMINAR_HOME/cfg/jobs/*.run`).
|
||||
- `show-running` shows the currently running jobs with their numbers.
|
||||
- `show-queued` shows the names of the jobs waiting in the queue.
|
||||
- `abort JOB NUMBER` manually aborts a currently running job by name and number.
|
||||
|
||||
`laminarc` connects to `laminard` using the address supplied by the `LAMINAR_HOST` environment variable. If it is not set, `laminarc` will first attempt to use `LAMINAR_BIND_RPC`, which will be available if `laminarc` is executed from a script within `laminard`. If neither `LAMINAR_HOST` nor `LAMINAR_BIND_RPC` is set, `laminarc` will assume a default host of `unix-abstract:laminar`.
|
||||
|
@ -173,8 +173,8 @@ int main(int argc, char** argv) {
|
||||
char* name = argv[2];
|
||||
*eq++ = '\0';
|
||||
char* val = eq;
|
||||
req.setJobName(job);
|
||||
req.setBuildNum(atoi(num));
|
||||
req.getRun().setJob(job);
|
||||
req.getRun().setBuildNum(atoi(num));
|
||||
req.getParam().setName(name);
|
||||
req.getParam().setValue(val);
|
||||
req.send().wait(waitScope);
|
||||
@ -182,6 +182,40 @@ int main(int argc, char** argv) {
|
||||
fprintf(stderr, "Missing $JOB or $RUN or param is not in the format key=value\n");
|
||||
return EINVAL;
|
||||
}
|
||||
} else if(strcmp(argv[1], "abort") == 0) {
|
||||
if(argc != 4) {
|
||||
fprintf(stderr, "Usage %s abort <jobName> <jobNumber>\n", argv[0]);
|
||||
return EINVAL;
|
||||
}
|
||||
auto req = laminar.abortRequest();
|
||||
req.getRun().setJob(argv[2]);
|
||||
req.getRun().setBuildNum(atoi(argv[3]));
|
||||
if(req.send().wait(waitScope).getResult() != LaminarCi::MethodResult::SUCCESS)
|
||||
ret = EFAILED;
|
||||
} else if(strcmp(argv[1], "show-jobs") == 0) {
|
||||
if(argc != 2) {
|
||||
fprintf(stderr, "Usage: %s show-jobs\n", argv[0]);
|
||||
return EINVAL;
|
||||
}
|
||||
for(auto it : laminar.listKnownRequest().send().wait(waitScope).getResult()) {
|
||||
printf("%s\n", it.cStr());
|
||||
}
|
||||
} else if(strcmp(argv[1], "show-queued") == 0) {
|
||||
if(argc != 2) {
|
||||
fprintf(stderr, "Usage: %s show-queued\n", argv[0]);
|
||||
return EINVAL;
|
||||
}
|
||||
for(auto it : laminar.listQueuedRequest().send().wait(waitScope).getResult()) {
|
||||
printf("%s\n", it.cStr());
|
||||
}
|
||||
} else if(strcmp(argv[1], "show-running") == 0) {
|
||||
if(argc != 2) {
|
||||
fprintf(stderr, "Usage: %s show-running\n", argv[0]);
|
||||
return EINVAL;
|
||||
}
|
||||
for(auto it : laminar.listRunningRequest().send().wait(waitScope).getResult()) {
|
||||
printf("%s:%d\n", it.getJob().cStr(), it.getBuildNum());
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Unknown command %s\n", argv[1]);
|
||||
return EINVAL;
|
||||
|
@ -128,6 +128,15 @@ struct LaminarInterface {
|
||||
// the environment of subsequent scripts.
|
||||
virtual bool setParam(std::string job, uint buildNum, std::string param, std::string value) = 0;
|
||||
|
||||
// Gets the list of jobs currently waiting in the execution queue
|
||||
virtual const std::list<std::shared_ptr<Run>>& listQueuedJobs() = 0;
|
||||
|
||||
// Gets the list of currently executing jobs
|
||||
virtual const RunSet& listRunningJobs() = 0;
|
||||
|
||||
// Gets the list of known jobs - scans cfg/jobs for *.run files
|
||||
virtual std::list<std::string> listKnownJobs() = 0;
|
||||
|
||||
// Fetches the content of an artifact given its filename relative to
|
||||
// $LAMINAR_HOME/archive. Ideally, this would instead be served by a
|
||||
// proper web server which handles this url.
|
||||
@ -143,6 +152,9 @@ struct LaminarInterface {
|
||||
// which handles this url.
|
||||
virtual std::string getCustomCss() = 0;
|
||||
|
||||
// Aborts a single job
|
||||
virtual bool abort(std::string job, uint buildNum) = 0;
|
||||
|
||||
// Abort all running jobs
|
||||
virtual void abortAll() = 0;
|
||||
|
||||
|
@ -5,7 +5,16 @@ interface LaminarCi {
|
||||
queue @0 (jobName :Text, params :List(JobParam)) -> (result :MethodResult);
|
||||
start @1 (jobName :Text, params :List(JobParam)) -> (result :MethodResult, buildNum :UInt32);
|
||||
run @2 (jobName :Text, params :List(JobParam)) -> (result :JobResult, buildNum :UInt32);
|
||||
set @3 (jobName :Text, buildNum :UInt32, param :JobParam) -> (result :MethodResult);
|
||||
set @3 (run :Run, param :JobParam) -> (result :MethodResult);
|
||||
listQueued @4 () -> (result :List(Text));
|
||||
listRunning @5 () -> (result :List(Run));
|
||||
listKnown @6 () -> (result :List(Text));
|
||||
abort @7 (run :Run) -> (result :MethodResult);
|
||||
|
||||
struct Run {
|
||||
job @0 :Text;
|
||||
buildNum @1 :UInt32;
|
||||
}
|
||||
|
||||
struct JobParam {
|
||||
name @0 :Text;
|
||||
|
@ -130,6 +130,25 @@ bool Laminar::setParam(std::string job, uint buildNum, std::string param, std::s
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::list<std::shared_ptr<Run>>& Laminar::listQueuedJobs() {
|
||||
return queuedJobs;
|
||||
}
|
||||
|
||||
const RunSet& Laminar::listRunningJobs() {
|
||||
return activeJobs;
|
||||
}
|
||||
|
||||
std::list<std::string> Laminar::listKnownJobs() {
|
||||
std::list<std::string> res;
|
||||
KJ_IF_MAYBE(dir, fsHome->tryOpenSubdir(kj::Path{"cfg","jobs"})) {
|
||||
for(kj::Directory::Entry& entry : (*dir)->listEntries()) {
|
||||
if(entry.type == kj::FsNode::Type::FILE && entry.name.endsWith(".run")) {
|
||||
res.emplace_back(entry.name.cStr(), entry.name.findLast('.').orDefault(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void Laminar::populateArtifacts(Json &j, std::string job, uint num) const {
|
||||
kj::Path runArchive{job,std::to_string(num)};
|
||||
@ -575,6 +594,14 @@ void Laminar::notifyConfigChanged()
|
||||
assignNewJobs();
|
||||
}
|
||||
|
||||
bool Laminar::abort(std::string job, uint buildNum) {
|
||||
if(Run* run = activeRun(job, buildNum)) {
|
||||
run->abort(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Laminar::abortAll() {
|
||||
for(std::shared_ptr<Run> run : activeJobs) {
|
||||
run->abort(false);
|
||||
|
@ -56,9 +56,13 @@ public:
|
||||
|
||||
void sendStatus(LaminarClient* client) override;
|
||||
bool setParam(std::string job, uint buildNum, std::string param, std::string value) override;
|
||||
const std::list<std::shared_ptr<Run>>& listQueuedJobs() override;
|
||||
const RunSet& listRunningJobs() override;
|
||||
std::list<std::string> listKnownJobs() override;
|
||||
kj::Maybe<kj::Own<const kj::ReadableFile>> getArtefact(std::string path) override;
|
||||
bool handleBadgeRequest(std::string job, std::string& badge) override;
|
||||
std::string getCustomCss() override;
|
||||
bool abort(std::string job, uint buildNum) override;
|
||||
void abortAll() override;
|
||||
void notifyConfigChanged() override;
|
||||
|
||||
|
@ -117,8 +117,8 @@ public:
|
||||
|
||||
// Set a parameter on a running build
|
||||
kj::Promise<void> set(SetContext context) override {
|
||||
std::string jobName = context.getParams().getJobName();
|
||||
uint buildNum = context.getParams().getBuildNum();
|
||||
std::string jobName = context.getParams().getRun().getJob();
|
||||
uint buildNum = context.getParams().getRun().getBuildNum();
|
||||
LLOG(INFO, "RPC set", jobName, buildNum);
|
||||
|
||||
LaminarCi::MethodResult result = laminar.setParam(jobName, buildNum,
|
||||
@ -129,6 +129,52 @@ public:
|
||||
return kj::READY_NOW;
|
||||
}
|
||||
|
||||
// List jobs in queue
|
||||
kj::Promise<void> listQueued(ListQueuedContext context) override {
|
||||
const std::list<std::shared_ptr<Run>>& queue = laminar.listQueuedJobs();
|
||||
auto res = context.getResults().initResult(queue.size());
|
||||
int i = 0;
|
||||
for(auto it : queue) {
|
||||
res.set(i++, it->name);
|
||||
}
|
||||
return kj::READY_NOW;
|
||||
}
|
||||
|
||||
// List running jobs
|
||||
kj::Promise<void> listRunning(ListRunningContext context) override {
|
||||
const RunSet& active = laminar.listRunningJobs();
|
||||
auto res = context.getResults().initResult(active.size());
|
||||
int i = 0;
|
||||
for(auto it : active) {
|
||||
res[i].setJob(it->name);
|
||||
res[i].setBuildNum(it->build);
|
||||
i++;
|
||||
}
|
||||
return kj::READY_NOW;
|
||||
}
|
||||
|
||||
// List known jobs
|
||||
kj::Promise<void> listKnown(ListKnownContext context) override {
|
||||
std::list<std::string> known = laminar.listKnownJobs();
|
||||
auto res = context.getResults().initResult(known.size());
|
||||
int i = 0;
|
||||
for(auto it : known) {
|
||||
res.set(i++, it);
|
||||
}
|
||||
return kj::READY_NOW;
|
||||
}
|
||||
|
||||
kj::Promise<void> abort(AbortContext context) override {
|
||||
std::string jobName = context.getParams().getRun().getJob();
|
||||
uint buildNum = context.getParams().getRun().getBuildNum();
|
||||
LLOG(INFO, "RPC abort", jobName, buildNum);
|
||||
LaminarCi::MethodResult result = laminar.abort(jobName, buildNum)
|
||||
? LaminarCi::MethodResult::SUCCESS
|
||||
: LaminarCi::MethodResult::FAILED;
|
||||
context.getResults().setResult(result);
|
||||
return kj::READY_NOW;
|
||||
}
|
||||
|
||||
private:
|
||||
// Helper to convert an RPC parameter list to a hash map
|
||||
ParamMap params(const capnp::List<LaminarCi::JobParam>::Reader& paramReader) {
|
||||
|
@ -49,8 +49,13 @@ public:
|
||||
MOCK_METHOD1(deregisterWaiter, void(LaminarWaiter* waiter));
|
||||
MOCK_METHOD1(sendStatus, void(LaminarClient* client));
|
||||
MOCK_METHOD4(setParam, bool(std::string job, uint buildNum, std::string param, std::string value));
|
||||
MOCK_METHOD0(listQueuedJobs, const std::list<std::shared_ptr<Run>>&());
|
||||
MOCK_METHOD0(listRunningJobs, const RunSet&());
|
||||
MOCK_METHOD0(listKnownJobs, std::list<std::string>());
|
||||
|
||||
MOCK_METHOD0(getCustomCss, std::string());
|
||||
MOCK_METHOD2(handleBadgeRequest, bool(std::string, std::string&));
|
||||
MOCK_METHOD2(abort, bool(std::string, uint));
|
||||
MOCK_METHOD0(abortAll, void());
|
||||
MOCK_METHOD0(notifyConfigChanged, void());
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user