-
Notifications
You must be signed in to change notification settings - Fork 72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Save information about child jobs in parent job #13
Conversation
import java.util.List; | ||
|
||
|
||
public class BuildInfoAction extends InvisibleAction { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see no problem with making it visible, but maybe it's outside the scope
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can put this comment to the code ;)
} | ||
|
||
public List<BuildInfo> getBuildInfoList() { | ||
return buildInfoList; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrap with unmodifyableList()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gone.
} | ||
|
||
public void addBuildInfo(String projectName, int buildNumber) { | ||
buildInfoList.add(new BuildInfo(projectName, buildNumber)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems there may be concurrency issues with this field. I would recommend CopyOnWrite list or synchronize
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CopyOnWrite looks like overkill, synchronize
needs some manual work, so, I picked up ConcurrentLinkedQueue
(current implementation of this action doesn't require random access).
private final String projectName; | ||
private final int buildNumber; | ||
|
||
public BuildInfo(String projectName, int buildNumber) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This approach may start leaking job existence fact if the API user has no Item.Discover
permission. Not sure if it's acceptable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How can I prevent this?
Btw, is it possible that user has access to list of actions of some build, but doesn't have access to the log of this build? (right now plugin publishes names/build numbers of triggered jobs to the log).
For now I made this class private and only list of runs can be retrieved from action. I hope that it would be safer.
* Make BuildInfoAction thread-safe * Create method to provide list of child runs * Add tests for BuildInfoAction
@@ -29,6 +30,15 @@ public void onStarted(Run<?, ?> run, TaskListener listener) { | |||
TaskListener taskListener = stepContext.get(TaskListener.class); | |||
// encodeTo(Run) calls getDisplayName, which does not include the project name. | |||
taskListener.getLogger().println("Starting building: " + ModelHyperlinkNote.encodeTo("/" + run.getUrl(), run.getFullDisplayName())); | |||
FlowNode parentNode = stepContext.get(FlowNode.class); | |||
if (parentNode != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@oleg-nenashev, I suppose that this code is not thread-safe as well. Is it correct?
I think to put synchronized(this)
for addAction/getAction
@oleg-nenashev, @jglick, can you take a look, please? |
@jglick, will you have time to review this change? Should I rebase it first? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
public List<Run<?, ?>> getChildBuilds() { | ||
List<Run<?, ?>> builds = new ArrayList<>(); | ||
|
||
for (BuildInfo buildInfo : buildInfos) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor optimization: Return Collections.emptyList()
if the size is null before creating a new ArrayList. Maybe also makes sense to pre-initialize builds
size to buildInfos.size
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if ConcurrentLinkedQueue.size()
is fast enough though. Maybe it will hammer the performance
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ConcurrentLinkedQueue.isEmpty()
comes for free, but in current implementation it's not possible to have empty collection of buildInfos
longer than several ms (+ StopTheWorld pause :) ). Anyway, I added this check anyway - who knows how this Action
will be used in future.
Pre-initialize - good idea, can decrease memory footprint a little bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few technical mistakes which could be easily corrected. More broadly, the intent of this PR is unclear, since it adds no user-visible functionality. If it is intended as an API for other plugins to consume, there would need to be at least one downstream PR demonstrating the integrated functionality, which could be used to evaluate the suitability of the API.
Thread.sleep(100); | ||
} | ||
|
||
assertEquals(501, buildInfoAction.getChildBuilds().size()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test does not seem to be proving anything very interesting. I would just delete it. (PowerMock is a pain in the butt to maintain for Jenkins plugins anyway.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now I used PowerMock
only for Jenkins singleton, for the rest of stuff I used Mockito. What do you prefer to use for mocking Jenkins.getInstance method?
BuildInfoAction() { | ||
} | ||
|
||
BuildInfoAction(String projectName, int buildNumber) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Useless constructor, better deleted.
for (BuildInfo buildInfo : buildInfos) { | ||
if (buildInfo != null) { | ||
Job job = Jenkins.getActiveInstance().getItemByFullName(buildInfo.getProjectName(), Job.class); | ||
if (buildInfo.getBuildNumber() != 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh? Why would the build number be zero?
if (buildInfo != null) { | ||
Job job = Jenkins.getActiveInstance().getItemByFullName(buildInfo.getProjectName(), Job.class); | ||
if (buildInfo.getBuildNumber() != 0) { | ||
builds.add((job != null) ? job.getBuildByNumber(buildInfo.getBuildNumber()) : null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the job is missing, do not add null.
And getBuildByNumber
may well add null; do not add that.
|
||
for (BuildInfo buildInfo : buildInfos) { | ||
if (buildInfo != null) { | ||
Job job = Jenkins.getActiveInstance().getItemByFullName(buildInfo.getProjectName(), Job.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unclear whether this should be impersonating SYSTEM
. Who is calling it? Perhaps better to return the raw data and let the caller decide how to handle missing information.
@@ -29,6 +30,11 @@ public void onStarted(Run<?, ?> run, TaskListener listener) { | |||
TaskListener taskListener = stepContext.get(TaskListener.class); | |||
// encodeTo(Run) calls getDisplayName, which does not include the project name. | |||
taskListener.getLogger().println("Starting building: " + ModelHyperlinkNote.encodeTo("/" + run.getUrl(), run.getFullDisplayName())); | |||
FlowNode parentNode = stepContext.get(FlowNode.class); | |||
if (parentNode != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to check for null if defined via @StepContextParameter
(which is actually deprecated but fixing that requires a larger change).
if (buildInfoAction == null) { | ||
synchronized (this) { | ||
buildInfoAction = parentNode.getAction(BuildInfoAction.class); | ||
if (buildInfoAction == null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Double-checked locking, unsafe.
|
||
private static class BuildInfo { | ||
private final String projectName; | ||
private final int buildNumber; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Easier just to use String
via Run.externalizableId
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does not look easier for me, in this case I would need one String concatenation + String.split
for every call for nothing. I now that it's tiny stuff, but even from code perspective it doesn't simplify much.
@@ -430,6 +450,23 @@ public MultiBranchProjectFactory newInstance() { | |||
} | |||
} | |||
|
|||
private void assertBuildInfoAction(WorkflowRun workflowRun, String projectName, int buildNumber) { | |||
FlowGraphWalker walker = new FlowGraphWalker(workflowRun.getExecution()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🐜 Deprecated, use DepthFirstScanner instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It needs workflow-api:2.17
, so, I updated it in pom.xml
as well, but now it fails in verification: https://ci.jenkins.io/blue/organizations/jenkins/Plugins%2Fpipeline-build-step-plugin/detail/PR-13/9/tests/
Also, I think @jglick would not be happy to introduce dependency for higher version (especially in case of test code needs it -> not production).
Also I didn't find tag @Deprecated
in constructor of FlowGraphWalker
class in new API.
@svanoort, @jglick, @oleg-nenashev So, what should I do?
I'll more or less echo Jesse's comments here -- would need to see an example of how this API is used in practice. |
@svanoort, @jglick I wanted to use it Build Failure Analyzer Plugin (@rsandell). I created proof of concept some time ago and it works perfect, but to create good PR I need to submit this change first. So, I wanted to use API presented in this PR in this way: https://github.com/Jimilian/build-failure-analyzer-plugin/blob/pipeline_jobs_poc/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/dbf/BuildFlowDBF.java |
#127 includes the same information, but additionally indicates which step was responsible for a given downstream build. |
In some cases it would be nice to know what exactly was triggered by pipeline job. I.e. this information can be used by Build Failure Analyzer Plugin to show failures in child jobs on the top trigger job.