提交 9ab5f567 编写于 作者: D dty

Allow jobs associated only through fingerprinted files to appear in the

dependency graph. This functionality is enabled through a feature flag,
hudson.tasks.Fingerprinter.enableFingerprintsInDependencyGraph, which is off
by default.

- Rebuild the dependency graph as part of each fingerprint action.
  Fingerprinter.buildDependencyGraph adds information to the dependency graph
  for projects found in fingerprint records. Since the dependency graph is
  based on project, once a relationship is established between two projects,
  we can skip scanning subsequent builds of the projects. Entries in the
  graph added by the fingerprinter action will never automatically trigger
  downstream builds.

   core/src/main/java/hudson/tasks/Fingerprinter.java

- When reporting dependency changes, don't consider builds that no longer
  exist. This handles issues when projects are renamed, and new projects are
  created using the same name, causing the change to point to incorrect
  builds.

   core/src/main/java/hudson/model/AbstractBuild.java

- Tests.

   test/src/test/java/hudson/tasks/FingerprinterTest.java
上级 211231d1
......@@ -1121,8 +1121,8 @@ public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends Abs
FingerprintAction o = from.getAction(FingerprintAction.class);
if (n==null || o==null) return Collections.emptyMap();
Map<AbstractProject,Integer> ndep = n.getDependencies();
Map<AbstractProject,Integer> odep = o.getDependencies();
Map<AbstractProject,Integer> ndep = n.getDependencies(true);
Map<AbstractProject,Integer> odep = o.getDependencies(true);
Map<AbstractProject,DependencyChange> r = new HashMap<AbstractProject,DependencyChange>();
......
......@@ -30,10 +30,15 @@ import hudson.FilePath;
import hudson.FilePath.FileCallable;
import hudson.Launcher;
import hudson.Util;
import hudson.matrix.MatrixConfiguration;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.Action;
import hudson.model.Build;
import hudson.model.BuildListener;
import hudson.model.DependecyDeclarer;
import hudson.model.DependencyGraph;
import hudson.model.DependencyGraph.Dependency;
import hudson.model.Fingerprint;
import hudson.model.Fingerprint.BuildPtr;
import hudson.model.FingerprintMap;
......@@ -41,10 +46,12 @@ import jenkins.model.Jenkins;
import hudson.model.Result;
import hudson.model.Run;
import hudson.model.RunAction;
import hudson.model.TaskListener;
import hudson.remoting.VirtualChannel;
import hudson.util.FormValidation;
import hudson.util.IOException2;
import hudson.util.PackedMap;
import hudson.util.RunList;
import net.sf.json.JSONObject;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.types.FileSet;
......@@ -58,10 +65,14 @@ import java.io.IOException;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
......@@ -71,8 +82,9 @@ import java.util.logging.Logger;
*
* @author Kohsuke Kawaguchi
*/
public class Fingerprinter extends Recorder implements Serializable {
public class Fingerprinter extends Recorder implements Serializable, DependecyDeclarer {
public static boolean enableFingerprintsInDependencyGraph = Boolean.parseBoolean(System.getProperty(Fingerprinter.class.getName() + ".enableFingerprintsInDependencyGraph", "false"));
/**
* Comma-separated list of files/directories to be fingerprinted.
*/
......@@ -124,6 +136,9 @@ public class Fingerprinter extends Recorder implements Serializable {
build.getActions().add(new FingerprintAction(build,record));
if (enableFingerprintsInDependencyGraph) {
Jenkins.getInstance().rebuildDependencyGraph();
}
} catch (IOException e) {
e.printStackTrace(listener.error(Messages.Fingerprinter_Failed()));
build.setResult(Result.FAILURE);
......@@ -137,6 +152,46 @@ public class Fingerprinter extends Recorder implements Serializable {
return BuildStepMonitor.NONE;
}
public void buildDependencyGraph(AbstractProject owner, DependencyGraph graph) {
if (enableFingerprintsInDependencyGraph) {
RunList builds = owner.getBuilds();
Set<String> seenUpstreamProjects = new HashSet<String>();
for ( ListIterator iter = builds.listIterator(); iter.hasNext(); ) {
Run build = (Run) iter.next();
List<FingerprintAction> fingerprints = build.getActions(FingerprintAction.class);
for (FingerprintAction action : fingerprints) {
Map<AbstractProject,Integer> deps = action.getDependencies();
for (AbstractProject key : deps.keySet()) {
if (key == owner) {
continue; // Avoid self references
}
AbstractProject p = key;
if (key instanceof MatrixConfiguration) {
p = key.getRootProject();
}
if (seenUpstreamProjects.contains(p.getName())) {
continue;
}
seenUpstreamProjects.add(p.getName());
graph.addDependency(new Dependency(p, owner) {
@Override
public boolean shouldTriggerBuild(AbstractBuild build,
TaskListener listener,
List<Action> actions) {
// Fingerprints should not trigger builds.
return false;
}
});
}
}
}
}
}
private void record(AbstractBuild<?,?> build, BuildListener listener, Map<String,String> record, final String targets) throws IOException, InterruptedException {
final class Record implements Serializable {
final boolean produced;
......@@ -353,10 +408,20 @@ public class Fingerprinter extends Recorder implements Serializable {
}
/**
* Gets the dependency to other builds in a map.
* Returns build numbers instead of {@link Build}, since log records may be gone.
* Gets the dependency to other existing builds in a map.
*/
public Map<AbstractProject,Integer> getDependencies() {
return getDependencies(false);
}
/**
* Gets the dependency to other builds in a map.
*
* @param includeMissing true if the original build should be included in
* the result, even if it doesn't exist
* @since 1.429
*/
public Map<AbstractProject,Integer> getDependencies(boolean includeMissing) {
Map<AbstractProject,Integer> r = new HashMap<AbstractProject,Integer>();
for (Fingerprint fp : getFingerprints().values()) {
......@@ -364,9 +429,11 @@ public class Fingerprinter extends Recorder implements Serializable {
if(bp==null) continue; // outside Hudson
if(bp.is(build)) continue; // we are the owner
AbstractProject job = bp.getJob();
if (job==null) continue; // no longer exists
if (job==null) continue; // project no longer exists
if (job.getParent()==build.getParent())
continue; // we are the parent of the build owner, that is almost like we are the owner
if(job.getBuildByNumber(bp.getNumber())==null && !includeMissing)
continue; // build no longer exists
Integer existing = r.get(job);
if(existing!=null && existing>bp.getNumber())
......
......@@ -72,6 +72,124 @@ public class FingerprinterTest extends HudsonTestCase {
private static final String renamedProject1 = "renamed project 1";
private static final String renamedProject2 = "renamed project 2";
@Override
public void setUp() throws Exception {
super.setUp();
Fingerprinter.enableFingerprintsInDependencyGraph = true;
}
public void testFingerprintDependencies() throws Exception {
FreeStyleProject upstream = createFreeStyleProjectWithFingerprints(singleContents, singleFiles);
FreeStyleProject downstream = createFreeStyleProjectWithFingerprints(singleContents, singleFiles);
FreeStyleBuild upstreamBuild = assertBuildStatusSuccess(upstream.scheduleBuild2(0).get());
FreeStyleBuild downstreamBuild = assertBuildStatusSuccess(downstream.scheduleBuild2(0).get());
List<AbstractProject> downstreamProjects = upstream.getDownstreamProjects();
List<AbstractProject> upstreamProjects = downstream.getUpstreamProjects();
assertEquals(1, downstreamProjects.size());
assertEquals(1, upstreamProjects.size());
assertTrue(upstreamProjects.contains(upstream));
assertTrue(downstreamProjects.contains(downstream));
}
public void testMultipleUpstreamDependencies() throws Exception {
FreeStyleProject upstream = createFreeStyleProjectWithFingerprints(singleContents, singleFiles);
FreeStyleProject upstream2 = createFreeStyleProjectWithFingerprints(singleContents2, singleFiles2);
FreeStyleProject downstream = createFreeStyleProjectWithFingerprints(doubleContents, doubleFiles);
FreeStyleBuild upstreamBuild = assertBuildStatusSuccess(upstream.scheduleBuild2(0).get());
FreeStyleBuild upstreamBuild2 = assertBuildStatusSuccess(upstream2.scheduleBuild2(0).get());
FreeStyleBuild downstreamBuild = assertBuildStatusSuccess(downstream.scheduleBuild2(0).get());
List<AbstractProject> downstreamProjects = upstream.getDownstreamProjects();
List<AbstractProject> downstreamProjects2 = upstream2.getDownstreamProjects();
List<AbstractProject> upstreamProjects = downstream.getUpstreamProjects();
assertEquals(1, downstreamProjects.size());
assertEquals(1, downstreamProjects2.size());
assertEquals(2, upstreamProjects.size());
assertTrue(upstreamProjects.contains(upstream));
assertTrue(upstreamProjects.contains(upstream2));
assertTrue(downstreamProjects.contains(downstream));
}
public void testMultipleDownstreamDependencies() throws Exception {
FreeStyleProject upstream = createFreeStyleProjectWithFingerprints(doubleContents, doubleFiles);
FreeStyleProject downstream = createFreeStyleProjectWithFingerprints(singleContents, singleFiles);
FreeStyleProject downstream2 = createFreeStyleProjectWithFingerprints(singleContents2, singleFiles2);
FreeStyleBuild upstreamBuild = assertBuildStatusSuccess(upstream.scheduleBuild2(0).get());
FreeStyleBuild downstreamBuild = assertBuildStatusSuccess(downstream.scheduleBuild2(0).get());
FreeStyleBuild downstreamBuild2 = assertBuildStatusSuccess(downstream2.scheduleBuild2(0).get());
List<AbstractProject> downstreamProjects = upstream.getDownstreamProjects();
List<AbstractProject> upstreamProjects = downstream.getUpstreamProjects();
List<AbstractProject> upstreamProjects2 = downstream2.getUpstreamProjects();
assertEquals(2, downstreamProjects.size());
assertEquals(1, upstreamProjects.size());
assertEquals(1, upstreamProjects2.size());
assertTrue(upstreamProjects.contains(upstream));
assertTrue(upstreamProjects2.contains(upstream));
assertTrue(downstreamProjects.contains(downstream));
assertTrue(downstreamProjects.contains(downstream2));
}
public void testDependencyExclusion() throws Exception {
FreeStyleProject upstream = createFreeStyleProjectWithFingerprints(singleContents, singleFiles);
FreeStyleProject downstream = createFreeStyleProjectWithFingerprints(singleContents, singleFiles);
FreeStyleBuild upstreamBuild = assertBuildStatusSuccess(upstream.scheduleBuild2(0).get());
FreeStyleBuild downstreamBuild = assertBuildStatusSuccess(downstream.scheduleBuild2(0).get());
upstreamBuild.delete();
Hudson.getInstance().rebuildDependencyGraph();
List<AbstractProject> upstreamProjects = downstream.getUpstreamProjects();
List<AbstractProject> downstreamProjects = upstream.getDownstreamProjects();
assertEquals(0, upstreamProjects.size());
assertEquals(0, downstreamProjects.size());
}
public void testCircularDependency() throws Exception {
FreeStyleProject p = createFreeStyleProjectWithFingerprints(singleContents, singleFiles);
FreeStyleBuild b1 = assertBuildStatusSuccess(p.scheduleBuild2(0).get());
FreeStyleBuild b2 = assertBuildStatusSuccess(p.scheduleBuild2(0).get());
List<AbstractProject> upstreamProjects = p.getUpstreamProjects();
List<AbstractProject> downstreamProjects = p.getDownstreamProjects();
assertEquals(0, upstreamProjects.size());
assertEquals(0, downstreamProjects.size());
}
public void testMatrixDependency() throws Exception {
MatrixProject matrixProject = createMatrixProject();
matrixProject.setAxes(new AxisList(new Axis("foo", "a", "b")));
FreeStyleProject freestyleProject = createFreeStyleProjectWithFingerprints(singleContents, singleFiles);
addFingerprinterToProject(matrixProject, singleContents, singleFiles);
hudson.rebuildDependencyGraph();
buildAndAssertSuccess(matrixProject);
buildAndAssertSuccess(freestyleProject);
waitUntilNoActivity();
RunList<FreeStyleBuild> builds = freestyleProject.getBuilds();
assertEquals("There should only be one FreestyleBuild", 1, builds.size());
FreeStyleBuild build = builds.iterator().next();
assertEquals(Result.SUCCESS, build.getResult());
List<AbstractProject> downstream = hudson.getDependencyGraph().getDownstream(matrixProject);
assertTrue(downstream.contains(freestyleProject));
List<AbstractProject> upstream = hudson.getDependencyGraph().getUpstream(freestyleProject);
assertTrue(upstream.contains(matrixProject));
}
public void testProjectRename() throws Exception {
FreeStyleProject upstream = createFreeStyleProjectWithFingerprints(singleContents, singleFiles);
FreeStyleProject downstream = createFreeStyleProjectWithFingerprints(singleContents, singleFiles);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册