mirror of
https://github.com/ohwgiles/laminar.git
synced 2024-10-27 20:34:20 +00:00
resolves #36: queue/start/run
This commit is contained in:
parent
828b66682d
commit
f1e4d10be3
@ -104,16 +104,35 @@ chmod +x /var/lib/laminar/cfg/hello.run
|
|||||||
|
|
||||||
# Triggering a run
|
# Triggering a run
|
||||||
|
|
||||||
To trigger a run of the `hello` job, execute
|
When triggering a run, the job is first added to a queue of upcoming tasks. If the server is busy, the job may wait in this queue for a while. It will only be assigned a job number when it leaves this queue and starts executing. The job number may be useful to the client that triggers the run, so there are a few ways to trigger a run.
|
||||||
|
|
||||||
|
To add the `hello` job to the queue ("fire-and-forget"), execute
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
laminarc trigger hello
|
laminarc queue hello
|
||||||
```
|
```
|
||||||
|
|
||||||
This causes the `/var/lib/laminar/cfg/hello.run` script to be executed, with a working directory of `/var/lib/laminar/run/hello/1`
|
In this case, laminarc returns immediately, with its error code indicating whether adding the job to the queue was sucessful.
|
||||||
|
|
||||||
|
To queue the job and wait until it leaves the queue and starts executing, use
|
||||||
|
|
||||||
|
```bash
|
||||||
|
laminarc start hello
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, laminarc blocks until the job starts executing, or returns immediately if queueing failed. The run number will be printed to standard output.
|
||||||
|
|
||||||
|
To launch and run the `hello` job to completion, execute
|
||||||
|
|
||||||
|
```bash
|
||||||
|
laminarc run hello
|
||||||
|
```
|
||||||
|
|
||||||
|
In all cases, a started run means the `/var/lib/laminar/cfg/hello.run` script will be executed, with a working directory of `/var/lib/laminar/run/hello/1` (or current run number)
|
||||||
|
|
||||||
The result and log output should be visible in the Web UI at http://localhost:8080/jobs/hello/1
|
The result and log output should be visible in the Web UI at http://localhost:8080/jobs/hello/1
|
||||||
|
|
||||||
|
|
||||||
## Isn't there a "Build Now" button I can click?
|
## Isn't there a "Build Now" button I can click?
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
@ -84,16 +84,16 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
auto& waitScope = client.getWaitScope();
|
auto& waitScope = client.getWaitScope();
|
||||||
|
|
||||||
if(strcmp(argv[1], "trigger") == 0) {
|
if(strcmp(argv[1], "queue") == 0) {
|
||||||
if(argc < 3) {
|
if(argc < 3) {
|
||||||
fprintf(stderr, "Usage %s trigger <jobName>\n", argv[0]);
|
fprintf(stderr, "Usage %s queue <jobName>\n", argv[0]);
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
kj::Vector<capnp::RemotePromise<LaminarCi::TriggerResults>> promises;
|
kj::Vector<capnp::RemotePromise<LaminarCi::QueueResults>> promises;
|
||||||
int jobNameIndex = 2;
|
int jobNameIndex = 2;
|
||||||
// make a request for each job specified on the commandline
|
// make a request for each job specified on the commandline
|
||||||
do {
|
do {
|
||||||
auto req = laminar.triggerRequest();
|
auto req = laminar.queueRequest();
|
||||||
req.setJobName(argv[jobNameIndex]);
|
req.setJobName(argv[jobNameIndex]);
|
||||||
int n = setParams(argc - jobNameIndex - 1, &argv[jobNameIndex + 1], req);
|
int n = setParams(argc - jobNameIndex - 1, &argv[jobNameIndex + 1], req);
|
||||||
promises.add(req.send());
|
promises.add(req.send());
|
||||||
@ -106,14 +106,39 @@ int main(int argc, char** argv) {
|
|||||||
return ENOENT;
|
return ENOENT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if(strcmp(argv[1], "run") == 0 || strcmp(argv[1], "start") == 0) {
|
} else if(strcmp(argv[1], "start") == 0 || strcmp(argv[1], "trigger") == 0) {
|
||||||
|
if(strcmp(argv[1], "trigger") == 0)
|
||||||
|
fprintf(stderr, "Warning: 'trigger' is deprecated, use 'queue' for the old behavior\n");
|
||||||
|
if(argc < 3) {
|
||||||
|
fprintf(stderr, "Usage %s queue <jobName>\n", argv[0]);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
kj::Vector<capnp::RemotePromise<LaminarCi::StartResults>> promises;
|
||||||
|
struct: public kj::TaskSet::ErrorHandler {
|
||||||
|
void taskFailed(kj::Exception&&) override {}
|
||||||
|
} ignoreFailed;
|
||||||
|
kj::TaskSet ts(ignoreFailed);
|
||||||
|
int jobNameIndex = 2;
|
||||||
|
// make a request for each job specified on the commandline
|
||||||
|
do {
|
||||||
|
auto req = laminar.startRequest();
|
||||||
|
req.setJobName(argv[jobNameIndex]);
|
||||||
|
int n = setParams(argc - jobNameIndex - 1, &argv[jobNameIndex + 1], req);
|
||||||
|
ts.add(req.send().then([&ret,argv,jobNameIndex](capnp::Response<LaminarCi::StartResults> resp){
|
||||||
|
if(resp.getResult() != LaminarCi::MethodResult::SUCCESS) {
|
||||||
|
fprintf(stderr, "Failed to start job '%s'\n", argv[2]);
|
||||||
|
ret = ENOENT;
|
||||||
|
}
|
||||||
|
printf("%s:%d\n", argv[jobNameIndex], resp.getBuildNum());
|
||||||
|
}));
|
||||||
|
jobNameIndex += n + 1;
|
||||||
|
} while(jobNameIndex < argc);
|
||||||
|
ts.onEmpty().wait(waitScope);
|
||||||
|
} else if(strcmp(argv[1], "run") == 0) {
|
||||||
if(argc < 3) {
|
if(argc < 3) {
|
||||||
fprintf(stderr, "Usage %s run <jobName>\n", argv[0]);
|
fprintf(stderr, "Usage %s run <jobName>\n", argv[0]);
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
if(strcmp(argv[1], "start") == 0) {
|
|
||||||
fprintf(stderr, "Warning: \"start\" is deprecated, please use \"run\" instead\n");
|
|
||||||
}
|
|
||||||
struct: public kj::TaskSet::ErrorHandler {
|
struct: public kj::TaskSet::ErrorHandler {
|
||||||
void taskFailed(kj::Exception&&) override {}
|
void taskFailed(kj::Exception&&) override {}
|
||||||
} ignoreFailed;
|
} ignoreFailed;
|
||||||
|
@ -2,11 +2,12 @@
|
|||||||
|
|
||||||
interface LaminarCi {
|
interface LaminarCi {
|
||||||
|
|
||||||
trigger @0 (jobName :Text, params :List(JobParam)) -> (result :MethodResult);
|
queue @0 (jobName :Text, params :List(JobParam)) -> (result :MethodResult);
|
||||||
run @1 (jobName :Text, params :List(JobParam)) -> (result :JobResult, buildNum :UInt32);
|
start @1 (jobName :Text, params :List(JobParam)) -> (result :MethodResult, buildNum :UInt32);
|
||||||
set @2 (jobName :Text, buildNum :UInt32, param :JobParam) -> (result :MethodResult);
|
run @2 (jobName :Text, params :List(JobParam)) -> (result :JobResult, buildNum :UInt32);
|
||||||
lock @3 (lockName :Text) -> ();
|
set @3 (jobName :Text, buildNum :UInt32, param :JobParam) -> (result :MethodResult);
|
||||||
release @4 (lockName :Text) -> ();
|
lock @4 (lockName :Text) -> ();
|
||||||
|
release @5 (lockName :Text) -> ();
|
||||||
|
|
||||||
struct JobParam {
|
struct JobParam {
|
||||||
name @0 :Text;
|
name @0 :Text;
|
||||||
|
@ -712,6 +712,9 @@ void Laminar::assignNewJobs() {
|
|||||||
c->sendMessage(msg);
|
c->sendMessage(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// notify the rpc client if the start command was used
|
||||||
|
run->started.fulfiller->fulfill();
|
||||||
|
|
||||||
// setup run completion handler
|
// setup run completion handler
|
||||||
run->notifyCompletion = [this](Run* r) { runFinished(r); };
|
run->notifyCompletion = [this](Run* r) { runFinished(r); };
|
||||||
|
|
||||||
|
@ -92,6 +92,7 @@ public:
|
|||||||
int fd;
|
int fd;
|
||||||
std::unordered_map<std::string, std::string> params;
|
std::unordered_map<std::string, std::string> params;
|
||||||
kj::Promise<void> timeout = kj::NEVER_DONE;
|
kj::Promise<void> timeout = kj::NEVER_DONE;
|
||||||
|
kj::PromiseFulfillerPair<void> started = kj::newPromiseAndFulfiller<void>();
|
||||||
|
|
||||||
time_t queuedAt;
|
time_t queuedAt;
|
||||||
time_t startedAt;
|
time_t startedAt;
|
||||||
|
@ -90,10 +90,10 @@ public:
|
|||||||
laminar.deregisterWaiter(this);
|
laminar.deregisterWaiter(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start a job, without waiting for it to finish
|
// Queue a job, without waiting for it to start
|
||||||
kj::Promise<void> trigger(TriggerContext context) override {
|
kj::Promise<void> queue(QueueContext context) override {
|
||||||
std::string jobName = context.getParams().getJobName();
|
std::string jobName = context.getParams().getJobName();
|
||||||
LLOG(INFO, "RPC trigger", jobName);
|
LLOG(INFO, "RPC queue", jobName);
|
||||||
ParamMap params;
|
ParamMap params;
|
||||||
for(auto p : context.getParams().getParams()) {
|
for(auto p : context.getParams().getParams()) {
|
||||||
params[p.getName().cStr()] = p.getValue().cStr();
|
params[p.getName().cStr()] = p.getValue().cStr();
|
||||||
@ -105,6 +105,26 @@ public:
|
|||||||
return kj::READY_NOW;
|
return kj::READY_NOW;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start a job, without waiting for it to finish
|
||||||
|
kj::Promise<void> start(StartContext context) override {
|
||||||
|
std::string jobName = context.getParams().getJobName();
|
||||||
|
LLOG(INFO, "RPC start", jobName);
|
||||||
|
ParamMap params;
|
||||||
|
for(auto p : context.getParams().getParams()) {
|
||||||
|
params[p.getName().cStr()] = p.getValue().cStr();
|
||||||
|
}
|
||||||
|
std::shared_ptr<Run> run = laminar.queueJob(jobName, params);
|
||||||
|
if(Run* r = run.get()) {
|
||||||
|
return r->started.promise.then([context,r]() mutable {
|
||||||
|
context.getResults().setResult(LaminarCi::MethodResult::SUCCESS);
|
||||||
|
context.getResults().setBuildNum(r->build);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
context.getResults().setResult(LaminarCi::MethodResult::FAILED);
|
||||||
|
return kj::READY_NOW;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Start a job and wait for the result
|
// Start a job and wait for the result
|
||||||
kj::Promise<void> run(RunContext context) override {
|
kj::Promise<void> run(RunContext context) override {
|
||||||
std::string jobName = context.getParams().getJobName();
|
std::string jobName = context.getParams().getJobName();
|
||||||
@ -115,11 +135,10 @@ public:
|
|||||||
}
|
}
|
||||||
std::shared_ptr<Run> run = laminar.queueJob(jobName, params);
|
std::shared_ptr<Run> run = laminar.queueJob(jobName, params);
|
||||||
if(const Run* r = run.get()) {
|
if(const Run* r = run.get()) {
|
||||||
uint num = r->build;
|
|
||||||
runWaiters[r].emplace_back(kj::newPromiseAndFulfiller<RunState>());
|
runWaiters[r].emplace_back(kj::newPromiseAndFulfiller<RunState>());
|
||||||
return runWaiters[r].back().promise.then([context,num](RunState state) mutable {
|
return runWaiters[r].back().promise.then([context,run](RunState state) mutable {
|
||||||
context.getResults().setResult(fromRunState(state));
|
context.getResults().setResult(fromRunState(state));
|
||||||
context.getResults().setBuildNum(num);
|
context.getResults().setBuildNum(run->build);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
context.getResults().setResult(LaminarCi::JobResult::UNKNOWN);
|
context.getResults().setResult(LaminarCi::JobResult::UNKNOWN);
|
||||||
|
@ -98,8 +98,8 @@ protected:
|
|||||||
Server* server;
|
Server* server;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(ServerTest, RpcTrigger) {
|
TEST_F(ServerTest, RpcQueue) {
|
||||||
auto req = client().triggerRequest();
|
auto req = client().queueRequest();
|
||||||
req.setJobName("foo");
|
req.setJobName("foo");
|
||||||
EXPECT_CALL(mockLaminar, queueJob("foo", ParamMap())).Times(testing::Exactly(1));
|
EXPECT_CALL(mockLaminar, queueJob("foo", ParamMap())).Times(testing::Exactly(1));
|
||||||
req.send().wait(ws());
|
req.send().wait(ws());
|
||||||
|
Loading…
Reference in New Issue
Block a user