Skip to content
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

JENKINS-43183 Can't see pipeline jobs in Global Build Stats #14

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>cloudbees-folder</artifactId>
<version>4.9</version>
</dependency>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-job</artifactId>
<version>2.0</version>
</dependency>
</dependencies>

<repositories>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,15 @@
import hudson.util.ChartUtil;
import hudson.util.FormValidation;
import java.io.File;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;


import javax.servlet.ServletException;

Expand All @@ -45,6 +50,8 @@
@ExportedBean
public class GlobalBuildStatsPlugin extends Plugin {

private static final Logger LOGGER = Logger.getLogger(GlobalBuildStatsPlugin.class.getName());

/**
* List of aggregated job build results
* This list will grow over time, but will be monthly sharded in different files to keep
Expand Down Expand Up @@ -95,12 +102,49 @@ public Api getApi() {
return new GlobalBuildStatsApi(this);
}

/**
* Lock used to synchronize Load/Save methods
**/
private final ReentrantLock SaveLoadLock = new ReentrantLock();

/**
* Will be set to true when plugin was loaded
*/
private boolean wasLoadedFirst = false;

/**
* Highered visibility of load method
* synchronized with save()
*/
@Override
public void load() throws IOException {
super.load();
this.SaveLoadLock.lock();
try{
super.load();
} finally {
wasLoadedFirst = true;
this.SaveLoadLock.unlock();
}
}

/**
* Highered visibility of save method
* synchronized with load()
*/
@Override
public void save() throws IOException {

while(wasLoadedFirst == false)
try{
Thread.sleep(1);
} catch(Exception e) {}

this.SaveLoadLock.lock();
try{
super.save();
} finally {
this.SaveLoadLock.unlock();
}
}

public File getConfigXmlFile() {
Expand Down Expand Up @@ -213,6 +257,31 @@ public void onDeleted(AbstractBuild build) {
getPluginBusiness().onBuildDeleted(build);
}
}

/**
* At the end of every pipeline job, let's gather job result informations into global build stats
* persisted data
*/

@Extension
public static class GlobalBuildStatsWorkflowRunListener extends RunListener<WorkflowRun> {
public GlobalBuildStatsWorkflowRunListener() {
super(WorkflowRun.class);
}

@Override
public void onCompleted(WorkflowRun w, TaskListener l) {
super.onCompleted(w, l);

getPluginBusiness().onJobCompleted(w);
}

@Override
public void onDeleted(WorkflowRun w) {
super.onDeleted(w);
getPluginBusiness().onBuildDeleted(w);
}
}

public static GlobalBuildStatsBusiness getPluginBusiness() {
// Retrieving global build stats plugin & adding build result to the registered build
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import hudson.plugins.global_build_stats.model.JobBuildResult;
import hudson.plugins.global_build_stats.model.JobBuildSearchResult;
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;

public class JobBuildResultFactory {

Expand All @@ -15,19 +16,31 @@ public class JobBuildResultFactory {
private JobBuildResultFactory(){
}

public JobBuildResult createJobBuildResult(AbstractBuild build){
String buildName = build.getProject().getFullName();
long duration = build.getDuration();
String nodeName = build.getBuiltOnStr();
/* Can't do that since MavenModuleSet is in maven-plugin artefact which is in test scope
if(build.getProject() instanceof MavenModuleSet){
buildName = ((MavenModuleSet)build.getProject()).getRootModule().toString();
}*/
return new JobBuildResult(createBuildResult(build.getResult()), buildName,
build.getNumber(), build.getTimestamp(), duration, nodeName, extractUserNameIn(build));
public JobBuildResult createJobBuildResult(Run r){
String builtOn = "pipeline";
String name = "";

if (r instanceof AbstractBuild) {
builtOn = ((AbstractBuild)r).getBuiltOnStr();
name = ((AbstractBuild)r).getProject().getFullName();
}

if (r instanceof WorkflowRun) {
name = ((WorkflowRun)r).getFullDisplayName().replace(" » " ,"/").replaceAll(" #[1-9]+$","");
}

return new JobBuildResult(
createBuildResult(r.getResult()),
name,
r.getNumber(),
r.getTimestamp(),
r.getDuration(),
builtOn,
extractUserNameIn(r)
);
}

public JobBuildSearchResult createJobBuildSearchResult(AbstractBuild build){
public JobBuildSearchResult createJobBuildSearchResult(Run build){
return createJobBuildSearchResult(createJobBuildResult(build));
}

Expand All @@ -49,7 +62,7 @@ public JobBuildSearchResult createJobBuildSearchResult(JobBuildResult r){
return new JobBuildSearchResult(r, isJobAccessible, isBuildAccessible);
}

public static String extractUserNameIn(AbstractBuild<?,?> build){
public static String extractUserNameIn(Run<?,?> build){
String userName;
@SuppressWarnings("deprecation") Cause.UserCause uc = build.getCause(Cause.UserCause.class);
Cause.UserIdCause uic = build.getCause(Cause.UserIdCause.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package hudson.plugins.global_build_stats.business;

import hudson.model.TopLevelItem;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.Hudson;
import hudson.model.Item;
import hudson.model.Job;
import hudson.model.Run;
import hudson.plugins.global_build_stats.GlobalBuildStatsPlugin;
import hudson.plugins.global_build_stats.JobBuildResultFactory;
import hudson.plugins.global_build_stats.model.*;
Expand Down Expand Up @@ -52,7 +51,7 @@ public GlobalBuildStatsBusiness(GlobalBuildStatsPlugin _plugin){
/**
* Records the result of a build.
*/
public void onJobCompleted(final AbstractBuild build) {
public void onJobCompleted(final Run build) {
for(RetentionStrategy s : plugin.getRetentionStrategies()){
s.onBuildCompleted(build, pluginSaver);
}
Expand Down Expand Up @@ -103,7 +102,7 @@ public void changePluginStateBeforeSavingIt(GlobalBuildStatsPlugin plugin) {
handleItem(jobBuildResultsRead,i);
}
}
if (item instanceof AbstractProject) {
if (item instanceof Job) {
handleItem(jobBuildResultsRead, item);
}
}
Expand All @@ -115,8 +114,8 @@ public void changePluginStateBeforeSavingIt(GlobalBuildStatsPlugin plugin) {
}

public void handleItem(List<JobBuildResult> results, TopLevelItem item){
if (item instanceof AbstractProject){
addBuildsFrom(results, (AbstractProject)item);
if (item instanceof Job){
addBuildsFrom(results, (Job)item);
}
}

Expand Down Expand Up @@ -366,13 +365,13 @@ public List<AbstractBuildStatChartDimension> createDataSetBuilder(BuildStatConfi
return dimensions;
}

private static void addBuild(List<JobBuildResult> jobBuildResultsRead, AbstractBuild build){
private static void addBuild(List<JobBuildResult> jobBuildResultsRead, Run build){
jobBuildResultsRead.add(JobBuildResultFactory.INSTANCE.createJobBuildResult(build));
}

private static void addBuildsFrom(List<JobBuildResult> jobBuildResultsRead, AbstractProject project){
List<AbstractBuild> builds = project.getBuilds();
Iterator<AbstractBuild> buildIterator = builds.iterator();
private static void addBuildsFrom(List<JobBuildResult> jobBuildResultsRead, Job project){
List<Run> builds = (List<Run>)project.getBuilds();
Iterator<Run> buildIterator = builds.iterator();

while (buildIterator.hasNext()) {
addBuild(jobBuildResultsRead, buildIterator.next());
Expand Down Expand Up @@ -404,7 +403,7 @@ public void reloadPlugin() {
}
}

public void onBuildDeleted(AbstractBuild build) {
public void onBuildDeleted(Run build) {
for(RetentionStrategy s : plugin.getRetentionStrategies()){
s.onBuildDeleted(build, pluginSaver);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.Date;

/**
* @author fcamblor
Expand Down Expand Up @@ -101,10 +102,14 @@ public void reloadPlugin() {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
} catch (NullPointerException x) {
File f = plugin.getConfigXmlFile();
File bak = new File(f.getParentFile(), f.getName() + ".bak");

Date stamp = new Date();

File bak = new File(f.getParentFile(), f.getName() + ".bak-"+stamp.getTime());
if (!f.renameTo(bak)) {
LOGGER.log(Level.WARNING, "failed to rename {0} to {1}", new Object[] {f, bak});
}

LOGGER.log(Level.WARNING, "JENKINS-17248 load failure; saving problematic file to " + bak, x);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,17 @@ public void applyQueuedResultsInFiles(){

for(String filename : updatedFilenames){
String jobResultFilepath = jobResultsRoot.getAbsolutePath() + File.separator + filename;
FileWriter fw = null;
try {
FileWriter fw = new FileWriter(jobResultFilepath);
fw = new FileWriter(jobResultFilepath);
Hudson.XSTREAM.toXML(persistedMonthlyResults.get(filename), fw);
fw.close();
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Unable to serialize job results into "+jobResultFilepath, e);
throw new IllegalStateException("Unable to serialize job results into "+jobResultFilepath, e);
} finally {
if (fw != null) try { fw.close(); } catch(Exception e) {}
}

}
LOGGER.log(Level.FINER, "Queued changes applied on job results !");
}
Expand All @@ -134,14 +137,33 @@ public static List<JobBuildResult> load(){
File jobResultsRoot = getJobResultFolder();
if(jobResultsRoot.exists()){
for(File f: jobResultsRoot.listFiles()){

if (f.getName().contains(".error-"))
continue;

FileReader fr=null;
try {
FileReader fr = new FileReader(f);
fr = new FileReader(f);
List<JobBuildResult> jobResultsInFile = (List<JobBuildResult>)Hudson.XSTREAM.fromXML(fr);
jobBuildResults.addAll(jobResultsInFile);
fr.close();
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Unable to read job results in "+f.getAbsolutePath(), e);
throw new IllegalStateException("Unable to read job results in "+f.getAbsolutePath(), e);

} catch (Exception e) {
try { fr.close(); } catch(Exception x) {};
fr = null;

Date stamp = new Date();
File bak = new File(f.getParentFile(), f.getName() + ".error-"+stamp.getTime());

LOGGER.log(Level.WARNING, "Unable to read job results in "+f.getAbsolutePath()+". Renaming to " + bak, e);

if (!f.renameTo(bak)) {
LOGGER.log(Level.WARNING, "failed to rename {0} to {1}", new Object[] {f, bak});
}

} finally {
if (fr != null) try { fr.close(); } catch(Exception e) {};
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package hudson.plugins.global_build_stats.rententionstrategies;

import hudson.model.AbstractBuild;
import hudson.model.Run;
import hudson.plugins.global_build_stats.GlobalBuildStatsPlugin;
import hudson.plugins.global_build_stats.JobBuildResultFactory;
import hudson.plugins.global_build_stats.business.GlobalBuildStatsPluginSaver;
Expand Down Expand Up @@ -47,7 +47,7 @@ public void strategyActivated(GlobalBuildStatsPluginSaver pluginSaver) {
purgeOldBuildResults(pluginSaver, System.currentTimeMillis());
}

public void buildCompleted(AbstractBuild buils, GlobalBuildStatsPluginSaver pluginSaver) {
public void buildCompleted(Run build, GlobalBuildStatsPluginSaver pluginSaver) {
final long now = System.currentTimeMillis();
if(lastPurgeDate == null || now > lastPurgeDate.getTime() + PURGE_FREQUENCY){
purgeOldBuildResults(pluginSaver, now);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package hudson.plugins.global_build_stats.rententionstrategies;

import hudson.model.AbstractBuild;
import hudson.model.Run;
import hudson.plugins.global_build_stats.GlobalBuildStatsPlugin;
import hudson.plugins.global_build_stats.JobBuildResultFactory;
import hudson.plugins.global_build_stats.business.GlobalBuildStatsPluginSaver;
Expand All @@ -18,7 +18,7 @@ public String getConfigPage() {
return "doNotKeepBuildResultWhenDiscarded.jelly";
}

public void buildDeleted(final AbstractBuild build, GlobalBuildStatsPluginSaver pluginSaver) {
public void buildDeleted(final Run build, GlobalBuildStatsPluginSaver pluginSaver) {
pluginSaver.updatePlugin(new GlobalBuildStatsPluginSaver.BeforeSavePluginCallback() {
@Override
public void changePluginStateBeforeSavingIt(GlobalBuildStatsPlugin plugin) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package hudson.plugins.global_build_stats.rententionstrategies;

import hudson.model.AbstractBuild;
import hudson.model.Run;
import hudson.plugins.global_build_stats.business.GlobalBuildStatsPluginSaver;
import hudson.plugins.global_build_stats.rententionstrategies.strategybehaviours.BuildCompletedListener;
import hudson.plugins.global_build_stats.rententionstrategies.strategybehaviours.BuildDeletedListener;
Expand Down Expand Up @@ -52,14 +52,14 @@ public void from(T strategyToCopy) {
}

// Overridable if retention strategy is a build deleted listener
public void onBuildDeleted(AbstractBuild build, GlobalBuildStatsPluginSaver pluginSaver) {
public void onBuildDeleted(Run build, GlobalBuildStatsPluginSaver pluginSaver) {
if(this instanceof BuildDeletedListener){
((BuildDeletedListener)this).buildDeleted(build, pluginSaver);
}
}

// Overridable if retention strategy is a build completed listener
public void onBuildCompleted(AbstractBuild build, GlobalBuildStatsPluginSaver pluginSaver) {
public void onBuildCompleted(Run build, GlobalBuildStatsPluginSaver pluginSaver) {
if(this instanceof BuildCompletedListener){
((BuildCompletedListener)this).buildCompleted(build, pluginSaver);
}
Expand Down
Loading