I am running a Declarative Pipeline where one of the steps runs a (very long) integration test. I'm trying to split my test into several smaller ones and run them in parallel over several nodes. I have 8 of these smaller tests and I have 8 nodes (under a label), so I'd like to have each test run on a separate node. Unfortunately, two tests — when run on the same node — interfere with each other, and so both fail.
I need to be able to first get the list of available nodes, and then run the smaller tests in parallel, one of each node; if there are not enough nodes, one of the smaller tests need to wait until the node is finished.
However, what happens is that when asking for a node by label, two of the smaller tests usually get the same node, and so both fail. Nodes are configured to run up to 3 executors, otherwise the whole system halts, so I can't change that.
My current configuration for the smaller test is:
stage('Integration Tests') {
when {
expression {params.TESTS_INTEGRATION}
}
parallel {
stage('Test1') {
agent {node {label 'my_builder'}}
steps {
script {
def shell_script = getShellScript("Test1")
sh "${shell_script}"
}
}
}
I am able to get the list of available slaves from a label like this:
pipeline {
stages {
// ... other stages here ...
stage('NodeList'){
steps {
script {
def nodes = getNodeNames('my_builder')
free_nodes = []
for (def element = 0; element < nodes.size(); element++) {
usenode = nodes[element]
try {
// Give it 5 seconds to run the nodetest function
timeout(time: 5, unit: 'SECONDS') {
node(usenode) {
nodetest()
free_nodes += usenode
}
}
} catch(err) {
}
}
println free_nodes
}
}
}
Where
def getNodeNames (String label) {
def lgroup = Jenkins.instance.getLabel(label)
def nodes = lgroup.getNodes()
def result = []
if (nodes.size() > 0) {
for (def element = 0; element < nodes.size(); element++) {
result += nodes[element].getNodeName()
}
}
return result
}
def nodetest() {
sh('echo alive on \$(hostname)')
}
How can I get the node name programmatically out of the free_nodes array and direct the stage to use that?
I've figured it out, so for the people from the future:
It turns out you can run a Scripted Pipeline inside a Declarative Pipeline, like this:
pipeline {
stage('SomeStage') {
steps {
script {
// ... your scripted pipeline here
}
}
}
The script can do anything, and that includes... running a pipeline!
Here is the script:
script {
def builders = [:]
def nodes = getNodeNames('my_label')
// let's find the free nodes
String[] free_nodes = []
for (def element = 0; element < nodes.size(); element++) {
usenode = nodes[element]
try {
// Give it 5 seconds to run the nodetest function
timeout(time: 5, unit: 'SECONDS') {
node(usenode) {
nodetest()
free_nodes += usenode
}
}
} catch(err) {
// do nothing
}
}
println free_nodes
def tests = params.TESTS_LIST.split(',')
for(int i = 0; i < tests.length; i++) {
// select the test to run
def the_test = tests[i]
// select on which node to run it
def the_node = free_nodes[i % free_nodes.length]
// here comes the scripted pipeline: prepare steps
builders[the_test] = {
// run on the selected node
node(the_node) {
// lock the resource with the name of the node so two tests can't run there at the same time
lock(the_node) {
// name the stage
stage(the_test) {
println "Running on ${NODE_NAME}"
def shell_script = getShellScript("${the_test}")
sh "${shell_script}"
}
}
}
}
}
// run the steps in parallel
parallel builders
}
Related
I'm trying to update the status of a job object. I get the "success" message return but the value is not updating. Do I miss something?
#nearBindgen
export class Contract {
private jobs: PersistentVector<Job> = new PersistentVector<Job>('jobs');
......
#mutateState()
cancelJob(jobTitle: string): string {
for (let i = 0; i < this.jobs.length; i++) {
if (this.jobs[i].title == jobTitle) {
this.jobs[i].status = "Cancelled";
return "success"
}
}
return "not found";
}
And I'm calling it like that:
near call apptwo.msaudi.testnet cancelJob '{\"jobTitle\":\"title2\"}' --account-id=msaudi.testnet
It’s not enough to update entry when you fetch it. You need to update the storage on the contract as well. Write it back in so to speak.
This isn’t enough
this.jobs[i].status = "Cancelled";
You need to add it back in:
if (this.jobs[i].title == jobTitle) {
const job: Job = this.jobs[i]; // Need an intermediate object in memory
job.status = "Cancelled";
this.jobs.replace(i, job); // Update storage with the new job.
return "success"
}
I have a loop that pushes back calls of std::async that are used to create objects in the pointed function and emplace them back to another vector. All the calls are pushed to the futures function and the results are ready when i used the VS debugger. However of the 507 calls, only 30 objects are actually created and i cant seem to pin point why.I have tried setting the launch policy to both async and defered but get the same result.
void load_sec_p(vector<Security>* secs, map<string, map<string, vector<daySec>>> *psa_timeline,security sec) {
Security tmp = Security(psa_timeline, &sec.tsymb, &sec.gicsInd);
std::lock_guard<std::mutex> lock(s_SecsMutex);
secs->emplace_back(tmp);
}
Above is the function being executed in the async call
below is the loop that pushes back the futures
for (auto& sec : security_list) {
m_SecFutures.emplace_back(std::async(load_sec_p,&async_secs, &psa_timeline, sec));
}
The following pictures show the watch of both variables after the above loop is completed and the entire future vectors is checked for completion.
I have tried creating the objects by just using a regular for loop and appending them synchronously but it simply just takes too long(2 hours and 11 minutes long). If anyone has any advice on alternatives or how to fix my vector problem it would be greatly appreciated.
The code that checks if all the futures is shown below:
bool done = false;
cout << "Waiting...";
do {
done = futures_ready(m_SecFutures);
} while (!done);
The function is
template<class T>
bool futures_ready(std::vector<std::future<T>>& futures) {
std::chrono::milliseconds span(5);
bool finished = false;
int pends = 0;
while (!finished) {
//allowing thread to sleep so futures can process a bit more and also
//so thread doesnt reach max cpu usage
std::this_thread::sleep_for(std::chrono::milliseconds(100));
for (auto& x : futures) {
if (x.wait_for(span) == std::future_status::timeout) {
++pends;
}
}
if (pends == 0) {
finished = true;
}
else {
pends = 0;
}
}
return finished;
}
I'm trying to create a new node in zookeeper via groovy, but after the system reboot the node disappear like it was never created.
This code is a part of Jenkins pipeline which in a post build groovy script against some windows VM. then the pipeline then executes a restart on this machine and the node vanishes.
I've this occurs on several other windows VMs
Here's my code:
#Grab('org.apache.zookeeper:zookeeper:3.4.6')
import org.apache.zookeeper.*
import static org.apache.zookeeper.ZooKeeper.States.*
import org.apache.zookeeper.ZooDefs.Ids;
final int TIMEOUT_MSEC = 5000
final int RETRY_MSEC = 100
def num_retries enter code here= 0
PATH_TO_NODE = "/some/path/to/node"
noOpWatcher = { event -> } as Watcher
zk = new ZooKeeper('myIP', TIMEOUT_MSEC, noOpWatcher)
def addNode() {
if (zk.exists(PATH_TO_NODE, true) == null) {
/*create node*/
println("creating nodes")
zk.create(PATH_TO_NODE, new byte[0], Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT)
zk.create(PATH_TO_NODE + "myNode", "someData".getBytes(), Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT)
} else {
println("node already exists")
}
}
while (zk.state != CONNECTED && num_retries < (TIMEOUT_MSEC / RETRY_MSEC)) {
Thread.sleep(RETRY_MSEC)
num_retries++
}
if (zk.state != CONNECTED) {
println("could not connect to zookeeper")
System.exit(1)
} else {
addNode()
}
zk.close()
I want to be able to change environment variables on Windows in a Jenkins pipeline, and only in that pipeline, how do I do it?
The environment variables are set in the system as:
XXX_DEV_DATA_DIR = E:\tools\jenkins\workspace\data\XXX-IDM
XXX_DEV_LIBS_DIR = E:\tools\jenkins\workspace\dev\libs
I tried the withEnv command but it has no effect:
node
{
withEnv(["XXX_DEV_LIBS_DIR=E:\\tools\\jenkins\\workspace\\dev\\libs', 'XXX_DEV_DATA_DIR=E:\\tools\\jenkins\\workspace\\data\\XXX-IDM-Testing"])
{
dir('E:\\tools\\jenkins\\workspace\\samples\\GetXXXSettings\\bin\\x64\\Release')
{
bat 'GetXXXSettings.exe'
}
}
}
The GetXXXSettings.exe application:
class Program
{
static void Main(string[] args)
{
var data = Environment.GetEnvironmentVariable("XXX_DEV_DATA_DIR");
var libs = Environment.GetEnvironmentVariable("XXX_DEV_LIBS_DIR");
Console.WriteLine("XXX ENVIRONMENT VARIABLES");
Console.WriteLine();
Console.WriteLine($"XXX_DEV_DATA_DIR = {data}");
Console.WriteLine($"XXX_DEV_LIBS_DIR = {libs}");
Console.WriteLine();
Console.WriteLine("END");
}
}
The result is as follows:
XXX ENVIRONMENT VARIABLES
XXX_DEV_DATA_DIR = E:\tools\jenkins\workspace\data\XXX-IDM
XXX_DEV_LIBS_DIR = E:\tools\jenkins\workspace\dev\libs', 'XXX_DEV_DATA_DIR=E:\tools\jenkins\workspace\data\XXX-IDM-Testing
END
The environment variable XXX_DEV_DATA_DIR is unchanged, I'm not sure what is happening to XXX_DEV_LIBS_DIR.
Looks like you issue with quotes at withEnv, try updating it
node
{
withEnv(['XXX_DEV_LIBS_DIR=E:\\tools\\jenkins\\workspace\\dev\\libs', 'XXX_DEV_DATA_DIR=E:\\tools\\jenkins\\workspace\\data\\XXX-IDM-Testing'])
{
dir('E:\\tools\\jenkins\\workspace\\samples\\GetXXXSettings\\bin\\x64\\Release')
{
bat 'GetXXXSettings.exe'
}
}
}
I want to retry if nightwatch does not click on an element. How can I do that?
I have the following code:
this.browser.isVisible('.signUp', function (result) {
if (result.value && result.length) {
for(var i = 0; i < 3; i += 1) {
this.browser.click(this.element.login.signInBtn);
var check = this.browser.url(function(result) {
// on login page
console.log(result);
if (result.value.indexOf("#login") !== -1) {
return false;
} else {
return true;
}
});
if (check) {
break;
}
}
}
}.bind(this));
-retries n will not run before and after function so this will not work as expected. You should try --suiteRetries n.
You can retry test cases with --retries command-line option. Example nightwatch --retries 2 will retry failed test case two additional times.
However I will not recommend you using that. I would first explore other options to make sure that element can be clicked. Like example waiting for it to become visible with waitForElementVisible
this.browser.waitForElementVisible(this.element.login.signInBtn, 1000);