Calling build.doStop() in prebuild is not stopping fast enough - jenkins

I am trying to write a Jenkins plugin that can automatically abort a build if that build is triggered on a holiday (or just a given input day/s). The user can configure the days, and each job gets a checkbox that allows the user to decide if they want their job aborted on a holiday or not. Currently my plugin extends JobProperty and utilizes global configuration where I have a list of blacklisted dates. If today is on my list of blacklisted days, then I do not want my job to run. The plugin "works" but with a few annoying caveats.
My main issue is that I can only FAIL the build if it happens to be triggered on a day that's one of my blacklisted days. This is a problem for me because there's no actual error. The job is operating as it should be and I don't want to receive emails full of errors just because the job was halted on a day I didn't want it to run (e.g: a holiday)
When my plugin decides to abort a build, I want to be able to end the job with an "Aborted" status. (In fact - I'd like to be able to control the status and leave it as a potential parameter.)
Below is my prebuild() code.
#Override
public boolean prebuild(AbstractBuild build, BuildListener listener) {
boolean stopped = false;
if(checkIfClosed) {
LocalDate today = LocalDate.now();
listener.getLogger().println("Checking the date for " + DateFormats.yyyyMMdd.print(today));
if (getDescriptor().getUseCalculatedDateChecker()) {
if (!NyseHolidayChecker.isMarketOpen(today)) {
listener.getLogger().println("Closed (From auto calculation)!");
stopped = true;
}
}
if (getDescriptor().getListOfClosedDates() != null && !getDescriptor().getListOfClosedDates().isEmpty()) {
if (getDescriptor().getListOfClosedDates().contains(DateFormats.yyyyMMdd.print(today))) {
listener.getLogger().println("Closed. Date is in the Closed Date List. " +
"If this is wrong check global configuration.");
stopped = true;
}
}
}
if(stopped) {
try {
if(build.doStop() == null) {
return false;
}
} catch (IOException e) {
listener.getLogger().println(e.getMessage());
return false;
} catch (ServletException e) {
listener.getLogger().println(e.getMessage());
return false;
}
//throw new RuntimeException("This job has been told not to run when marked as Closed!");
//throw new AbortException("This job has been told not to run when marked as Closed!");
}
return true;
}
I've tried several different ways to get the job to abort immediately and not have the build marked as failed.
The doc tells me that I should throw an AbortException but that does not appear to be supported by the function I'm overriding.
I also tried to call doStop() but then my first build step (out of 2) still runs at least a little. This isn't desirable because I will never know what state my job will be in when it will be aborted (it could have already sshed somewhere and killed a process...or made something live etc)
What am I missing? I feel like I'm hacking around to get what I need. I am hoping someone can point me in the right direction on how to best do this.

Digging further into Jenkins code showed the Build.doRun() method will execute the build steps in a do while loop which was allowing for a small bit of the build step to get through. The AbortException which Jenkins documentation recommends will also mark a build as failure (undesirable). The only means I found to cancel a job and have it properly mark as just aborted was to throw an InterruptedException. The JobProperties prebuild() function does not allow for any throws (other than Runtime which will mark as failure).
The plugin now extends BuildWrapper. This has both a setUp and preCheckout method that will run prior to build steps executing. These two methods can also throw InterruptedExceptions. Now if my checks pass and the date is blacklisted an InterruptedException is thrown from the interruptOnHoliday(...) method. The BuildWrapper also utilizes a BuildWrapperDescriptor which will place a checkbox in the job configuration based on the name provided in the overridden getDisplayName() function (Acts sort of as a jetty optionalBlock). With this box checked the setUp function can get called, otherwise it will not. This effectively makes the holiday check plugin optional (desired).
To give help to a user on what the Plugin does, include a help.html file in the resources for the plugin, this will allow the programmer to explain to the user how and why to use the Plugin. The BuildWrapperDescriptor class will programmatically know how to use that file.
Hope this helps everyone. Below is the new way to kill :
#Override
public void preCheckout(AbstractBuild build, Launcher launcher, BuildListener listener)
throws InterruptedException, IOException {
interruptOnHoliday(build, listener);
}
/**
* Run a check against todays date and dates from the {#link NyseHolidayChecker}
* which is an automatic calculator for New York Stock Exchange holidays and also a check against the manually
* created user date list. If either one is not checked to be used, then they will be ignored.
*
* #param build {#link hudson.model.AbstractBuild} that we are on, will kill the executor
* #param listener {#link hudson.model.BuildListener} that we will log to
*
* #throws InterruptedException
*/
private void interruptOnHoliday(AbstractBuild build, BuildListener listener) throws InterruptedException {
boolean stopped = false;
LocalDate today = LocalDate.now();
listener.getLogger().println("Checking the date for " + DateFormats.yyyyMMdd.print(today));
//if the NYSE calculator is checked then let's use it
if (getDescriptor().getUseNyseCalculatedDateChecker()) {
if (!NyseHolidayChecker.isMarketOpen(today)) {
listener.getLogger().println("The NYSE is not Open today (From auto calculation)" +
" and this job is marked to abort. Stopping the build!");
stopped = true;
}
}
//If we have inserted manual dates into the list we want to check them
if (getDescriptor().getListOfClosedDates() != null && !getDescriptor().getListOfClosedDates().isEmpty()) {
if (getDescriptor().getListOfClosedDates().contains(DateFormats.yyyyMMdd.print(today))) {
listener.getLogger().println("This date is blacklisted, and this job is marked to abort. " +
"Stopping the job! If this date should not be on the list check Jenkins Settings.");
stopped = true;
}
}
//if we should stop the job then we call doStop() on the build and we also throw an InterruptedException
//The InterruptedException is the only way to abort a build without it failing.
if (stopped) {
try {
build.doStop();
throw new InterruptedException(DateFormats.yyyyMMdd.print(today) + " is a blacklisted date.");
} catch (IOException e) {
listener.getLogger().println(e.getMessage());
} catch (ServletException e) {
listener.getLogger().println(e.getMessage());
}
}
}
#Override
public BuildWrapper.Environment setUp(AbstractBuild build, Launcher launcher, BuildListener listener)
throws InterruptedException, IOException {
interruptOnHoliday(build, listener);
return new CloseCheckerEnvironment();
}

Related

Quit a specflow scenario, or avoid execution based on a precondition

I want to be able to avoid a specflow scenario execution, or quit at the first step, depending on external configuration. Is that possible?
Like
Background:
Given we have a satisfied precondition
the precondition fails and the scenario stops without returning an error
IUnitTestRuntimeProvider interface should be injected
https://docs.specflow.org/projects/specflow/en/latest/Execution/SkippingScenarios.html
however, I then run into the fact that this interface can not be resolved
https://github.com/SpecFlowOSS/SpecFlow/issues/2076
the resolution of this does not work for me in a .Net 5, XUnit project, with Visual Studio 2019
If a step does not throw an exception, then the step passes. You could refactor that particular step into a private method, then simply wrap the method call in a try-catch inside an if statement:
[Given(#"we have a satisfied precondition")]
public void GivenWeHaveASatisfiedPrecondition()
{
if (/* check your external config here */)
{
try
{
PerformPrecondition();
}
catch (Exception ex)
{
Console.WriteLine("Step failed, but continuing scenario." + Environment.NewLine + Environment.NewLine + ex);
}
}
else
{
// Failure in this step should fail the scenario.
PerformPrecondition();
}
}
private void PerformPrecondition()
{
// Your step logic goes here
}
You haven't specified where your external configuration is at, but there are a thousand ways to do this.

Jenkins plugin - how to get currently executing job?

I am building a jenkins pipeline plugin (methods to be invoked from a pipeline) and need to get retrieve information about the currently running job, which invoked my methods.
There are a couple of questions I found talking about it, for example here - Jenkins Plugin How to get Job information.
Yet I can't figure out how to use this information. I do have access to the Jenkins instance, but don't have any info about the current project, job, build, etc. How can I get hold of that info?
Note, this is a pipeline steps plugin, there is no perform method in it.
Ok, after search, I finally found the answer in the most obvious of all places - documentation for writing pipeline steps plugins and the corresponding API documentation.
The way to do it is from the Execution class. Inside it, just call getContext(), which returns StepContext, which then has .get method to get the rest of the things you need:
public class MyExecution extends SynchronousNonBlockingStepExecution<ReturnType> {
...
#Override
protected ReturnType run() throws Exception {
try {
StepContext context = getContex();
// get currently used workspace path
FilePath path = context.get(FilePath.class);
//get current run
Run run = context.get(Run.class);
// ... and so on ...
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
...
}

TFS Build (2013) - Build Stop Handling

I have custom codeactivities in TFS Build. One of them is a background thread that TFS Build does not know about.
I want to find out if there is a way for this thread to check if a "stop build" has been requested. (i.e. check on the current build status WITHOUT needing a CodeActivityContext)
(NB: I can't use the AsyncCodeActivity (and its cancel mechanism) because this still blocks subsequent tasks)
I am currently using a heartbeat system and relying on a timeout of the heatbeat from the TFS Build flow loop but this is not fool proof.
IBuildDetail.BuildFinished exists but there is the catch 22 of if the build is finished, how do you get iBuildDetail?
Because code activities are "stateless", then using a previous "CodeActivityContext" to get iBuildDetail does not work, i.e. the context no longer exists.
I can get to a code path of _buildServer.GetBuild(_buildUri)
but can't find out how to establish your current builduri (not to be confused with build definition, server, agent or number)
Thanks
Good news, I found a solution
I was caught up in the BuildUri term, it turns out at the bottom of iBuildDetails, is "Uri"
This turns out to be the BuildUri
Thus code like this works ...
static private Uri _buildUri;
static private IBuildServer _buildServer;
...
protected override void Execute(CodeActivityContext context)
{
// Obtain the runtime value of the Text input argument
string ScriptPath = context.GetValue(this.InScriptPath);
_thisContext = context;
IBuildDetail buildDetail = _thisContext.GetExtension<IBuildDetail>();
_buildUri = buildDetail.Uri; // This is the complete string
_buildServer = buildDetail.BuildServer;
......
In background tthread
try
{
IBuildDetail buildDetail = _buildServer.GetBuild(_buildUri); // this does not work as it is not the BuildUri
if (buildDetail != null)
{
if (buildDetail.Status == BuildStatus.Stopped)
{
TerminateProcess();
}
}
}
catch (Exception Ex)
{
TerminateProcess();
}

Timer job not listed in Timer Job Definitions + Sharepoint 2007

I have created a Custom Timer job which is added when a feature gets activated. The feature does get activated without any exception. But the timer job is not being listed in Timer Job definitions on Central Admin.
The same is working on Staging Server but I am facing this in Production Server.
I am working on Sharepoint2007.
Below is what I have done in Feature Activated.
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
try
{
SPSecurity.RunWithElevatedPrivileges(delegate()
{
SPWeb parentWeb = properties.Feature.Parent as SPWeb;
UpdateEmpReferralListTimer taskJob;
SPMinuteSchedule schedule;
foreach (SPJobDefinition job in parentWeb.Site.WebApplication.JobDefinitions)
{
if (job.Name == "xyz")
job.Delete();
}
parentWeb = properties.Feature.Parent as SPWeb;
taskJob = new UpdateEmpReferralListTimer("xyz", parentWeb.Site.WebApplication);
schedule = new SPMinuteSchedule();
schedule.BeginSecond = 0;
schedule.EndSecond = 59;
taskJob.Schedule = schedule;
taskJob.Update();
});
}
catch (Exception Ex)
{
string str = Ex.Message.ToString();
}
}
Scope of Feature is "Web"
To my knowledge trying to perform tasks that require administrator privilegies (like creating timer job) should not work from site level feature activation callback if SharePoint accounts set up properly. Account that web sites run under normally does not have adminstrative privilegies.
It likely works on your staging server due to all accounts having the same privileges. Check out SharePoint logs on your production server to see what exception is (after removing code that eats exception without rethrowing/reporting it - catch (Exception Ex) {...}).
how do you know that your feature is getting activated without error. your code has try/catch block and you are doing nothing in catch block. if your code throwing any error, your catch block will suppress it.
add this line in your catch block and check sharepoint log.
Microsoft.SharePoint.Administration.SPDiagnosticsService.Local.WriteTrace(0, new SPDiagnosticsCategory("Development Debugging", TraceSeverity.Unexpected, EventSeverity.Information), TraceSeverity.Unexpected, str, null);
you can find log files in C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\LOGS folder

How do I read a multiline value using the Ant 'input' task?

Anyone know how I can enter a multiline value in an Ant script? I'm prompting the user for a Subversion commit comment using the input task, and I'd like to be able to support multiple lines of text.
I'm running the standalone version of Ant at the Windows command prompt.
I thought I might be able to do a search and replace for \n, but I can't see any easy way to do a replace from property value to property value in Ant. It looks like I'd have to write a file, replace in the file, and then load the file into another property. I don't want it that badly.
I'm not 100% positive about this, but I took a look at the Ant source code, and it just does a readLine():
From /org/apache/tools/ant/input/DefaultInputHandler.java:
/**
* Prompts and requests input. May loop until a valid input has
* been entered.
* #param request the request to handle
* #throws BuildException if not possible to read from console
*/
public void handleInput(InputRequest request) throws BuildException {
String prompt = getPrompt(request);
BufferedReader r = null;
try {
r = new BufferedReader(new InputStreamReader(getInputStream()));
do {
System.err.println(prompt);
System.err.flush();
try {
String input = r.readLine();
request.setInput(input);
} catch (IOException e) {
throw new BuildException("Failed to read input from"
+ " Console.", e);
}
} while (!request.isInputValid());
} finally {
if (r != null) {
try {
r.close();
} catch (IOException e) {
throw new BuildException("Failed to close input.", e);
}
}
}
}
Here is what I would do if I were you:
If you are using Ant 1.7, then try implementing your own InputHandler, as described in the documentation. The Apache License permits you to basically copy-and-paste the above code as a starting point.
If you are using Ant 1.6 or earlier, then just create your own MultiLineInput task. You can extend the existing Input class and just read multiple lines.
In either case, you would need to decide how the user indicates "I'm done." You could use a blank line or a period or something.
Good luck!
P.S. When I did a Google search for "ant multi-line input", this page was the first hit :-). Pretty impressive for a question that was asked less than an hour ago.

Resources