View Javadoc
1   package org.kuali.common.devops.jenkins.upgrade.model;
2   
3   import static com.google.common.base.Optional.absent;
4   import static com.google.common.base.Preconditions.checkNotNull;
5   import static com.google.common.collect.Iterables.filter;
6   import static com.google.common.collect.Lists.newArrayList;
7   import static com.google.common.collect.Ordering.natural;
8   import static java.lang.String.format;
9   import static org.kuali.common.core.io.Files.buildUnixFile;
10  import static org.kuali.common.core.io.Files.checkIsDir;
11  import static org.kuali.common.devops.jenkins.upgrade.Jenkins.BUILDS_DIRECTORY;
12  import static org.kuali.common.devops.jenkins.upgrade.Jenkins.buildNumberMap;
13  import static org.kuali.common.devops.jenkins.upgrade.Jenkins.isJenkinsBuildDirectory;
14  import static org.kuali.common.devops.jenkins.upgrade.Jenkins.listBuildDirContents;
15  import static org.kuali.common.util.base.Precondition.checkNotBlank;
16  import static org.kuali.common.util.base.Precondition.checkNotNull;
17  import static org.kuali.common.util.log.Loggers.newLogger;
18  
19  import java.io.IOException;
20  import java.nio.file.Path;
21  import java.nio.file.Paths;
22  import java.util.List;
23  import java.util.Map;
24  
25  import org.kuali.common.core.io.UnixFile;
26  import org.slf4j.Logger;
27  
28  import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
29  import com.google.common.base.Optional;
30  import com.google.common.base.Predicate;
31  import com.google.common.collect.ImmutableList;
32  
33  @JsonDeserialize(builder = JobSummary.Builder.class)
34  public final class JobSummary {
35  
36  	private static final Logger logger = newLogger();
37  
38  	private final String name;
39  	private final UnixFile basedir;
40  	private final ImmutableList<BuildSummary> builds;
41  	private final Optional<BuildSummary> lastFailedBuild;
42  	private final Optional<BuildSummary> lastStableBuild;
43  	private final Optional<BuildSummary> lastSuccessfulBuild;
44  	private final Optional<BuildSummary> lastUnstableBuild;
45  	private final Optional<BuildSummary> lastUnsuccessfulBuild;
46  
47  	public static JobSummary build(UnixFile basedir, String timeZoneId, long startedBefore) throws IOException {
48  		checkIsDir(basedir);
49  		List<UnixFile> files = listBuildDirContents(basedir);
50  		Map<String, Integer> buildNumbers = buildNumberMap(files);
51  		List<BuildSummary> builds = getBuilds(files, buildNumbers, timeZoneId, startedBefore);
52  		String name = basedir.getFileName();
53  		Builder builder = builder().withBasedir(basedir).withBuilds(builds).withName(name);
54  		builder.withLastFailedBuild(getNamedBuild(basedir, "lastFailedBuild", builds));
55  		builder.withLastStableBuild(getNamedBuild(basedir, "lastStableBuild", builds));
56  		builder.withLastSuccessfulBuild(getNamedBuild(basedir, "lastSuccessfulBuild", builds));
57  		builder.withLastUnstableBuild(getNamedBuild(basedir, "lastUnstableBuild", builds));
58  		builder.withLastUnsuccessfulBuild(getNamedBuild(basedir, "lastUnsuccessfulBuild", builds));
59  		return builder.build();
60  	}
61  
62  	private static Optional<BuildSummary> getNamedBuild(UnixFile jobDir, String link, List<BuildSummary> builds) {
63  		try {
64  			Path path = Paths.get(jobDir.getPath() + "/" + BUILDS_DIRECTORY + "/" + link);
65  			UnixFile file = buildUnixFile(path);
66  			if (!file.getAttributes().isSymbolicLink()) {
67  				return absent();
68  			}
69  			Path realPath = path.toRealPath();
70  			UnixFile realFile = buildUnixFile(realPath);
71  			for (BuildSummary build : builds) {
72  				if (realFile.equals(build.getBasedir())) {
73  					return Optional.of(build);
74  				}
75  			}
76  			return absent();
77  		} catch (IOException e) {
78  			return absent();
79  		}
80  	}
81  
82  	private static List<BuildSummary> getBuilds(List<UnixFile> files, Map<String, Integer> buildNumbers, String timeZoneId, long startedBefore) throws IOException {
83  		// Setup a predicate that filters out everything except timestamp'd build directories
84  		Predicate<UnixFile> buildsOnly = isJenkinsBuildDirectory();
85  
86  		// Convert Path objects into UnixFile objects and sort them
87  		List<UnixFile> list = natural().sortedCopy(filter(files, buildsOnly));
88  
89  		// Convert each build directory into a BuildSummary object
90  		List<BuildSummary> builds = newArrayList();
91  		for (UnixFile element : list) {
92  			try {
93  				int buildNumber = checkNotNull(buildNumbers.get(element.getPath()), "no build number for [%s]", element.getPath());
94  				BuildSummary build = BuildSummary.build(element, buildNumber, timeZoneId);
95  				if (build.getStartTime() > startedBefore) {
96  					continue;
97  				}
98  				if (!build.getFiles().isEmpty()) {
99  					builds.add(build);
100 				}
101 			} catch (IOException e) {
102 				// We don't have exclusive access to the file system
103 				// Jenkins may delete a build while we are working
104 				logger.info(format("error -> [%s]", e));
105 			}
106 		}
107 		return builds;
108 	}
109 
110 	public static Builder builder() {
111 		return new Builder();
112 	}
113 
114 	private JobSummary(Builder builder) {
115 		this.name = builder.name;
116 		this.basedir = builder.basedir;
117 		this.builds = ImmutableList.copyOf(builder.builds);
118 		this.lastFailedBuild = builder.lastFailedBuild;
119 		this.lastStableBuild = builder.lastStableBuild;
120 		this.lastSuccessfulBuild = builder.lastSuccessfulBuild;
121 		this.lastUnstableBuild = builder.lastUnstableBuild;
122 		this.lastUnsuccessfulBuild = builder.lastUnsuccessfulBuild;
123 	}
124 
125 	public static class Builder implements org.apache.commons.lang3.builder.Builder<JobSummary> {
126 
127 		private String name;
128 		private UnixFile basedir;
129 		private List<BuildSummary> builds = newArrayList();
130 		private Optional<BuildSummary> lastFailedBuild = absent();
131 		private Optional<BuildSummary> lastStableBuild = absent();
132 		private Optional<BuildSummary> lastSuccessfulBuild = absent();
133 		private Optional<BuildSummary> lastUnstableBuild = absent();
134 		private Optional<BuildSummary> lastUnsuccessfulBuild = absent();
135 
136 		public Builder withLastUnsuccessfulBuild(Optional<BuildSummary> lastUnsuccessfulBuild) {
137 			this.lastUnsuccessfulBuild = lastUnsuccessfulBuild;
138 			return this;
139 		}
140 
141 		public Builder withLastUnstableBuild(Optional<BuildSummary> lastUnstableBuild) {
142 			this.lastUnstableBuild = lastUnstableBuild;
143 			return this;
144 		}
145 
146 		public Builder withLastSuccessfulBuild(Optional<BuildSummary> lastSuccessfulBuild) {
147 			this.lastSuccessfulBuild = lastSuccessfulBuild;
148 			return this;
149 		}
150 
151 		public Builder withLastStableBuild(Optional<BuildSummary> lastStableBuild) {
152 			this.lastStableBuild = lastStableBuild;
153 			return this;
154 		}
155 
156 		public Builder withLastFailedBuild(Optional<BuildSummary> lastFailedBuild) {
157 			this.lastFailedBuild = lastFailedBuild;
158 			return this;
159 		}
160 
161 		public Builder withBasedir(UnixFile basedir) {
162 			this.basedir = basedir;
163 			return this;
164 		}
165 
166 		public Builder withName(String name) {
167 			this.name = name;
168 			return this;
169 		}
170 
171 		public Builder withBuilds(List<BuildSummary> builds) {
172 			this.builds = builds;
173 			return this;
174 		}
175 
176 		@Override
177 		public JobSummary build() {
178 			return validate(new JobSummary(this));
179 		}
180 
181 		private static JobSummary validate(JobSummary instance) {
182 			checkNotBlank(instance.name, "name");
183 			checkNotNull(instance.basedir, "basedir");
184 			checkNotNull(instance.builds, "builds");
185 			return instance;
186 		}
187 	}
188 
189 	public List<BuildSummary> getBuilds() {
190 		return builds;
191 	}
192 
193 	public String getName() {
194 		return name;
195 	}
196 
197 	public UnixFile getBasedir() {
198 		return basedir;
199 	}
200 
201 	public Optional<BuildSummary> getLastFailedBuild() {
202 		return lastFailedBuild;
203 	}
204 
205 	public Optional<BuildSummary> getLastStableBuild() {
206 		return lastStableBuild;
207 	}
208 
209 	public Optional<BuildSummary> getLastSuccessfulBuild() {
210 		return lastSuccessfulBuild;
211 	}
212 
213 	public Optional<BuildSummary> getLastUnstableBuild() {
214 		return lastUnstableBuild;
215 	}
216 
217 	public Optional<BuildSummary> getLastUnsuccessfulBuild() {
218 		return lastUnsuccessfulBuild;
219 	}
220 
221 }