1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.whitesource.teamcity.agent;
17
18 import com.intellij.openapi.diagnostic.Logger;
19 import jetbrains.buildServer.ExtensionHolder;
20 import jetbrains.buildServer.agent.*;
21 import jetbrains.buildServer.log.Loggers;
22 import jetbrains.buildServer.util.EventDispatcher;
23 import jetbrains.buildServer.util.StringUtil;
24 import org.apache.commons.lang.StringUtils;
25 import org.jetbrains.annotations.NotNull;
26 import org.springframework.util.CollectionUtils;
27 import org.whitesource.agent.api.dispatch.CheckPoliciesResult;
28 import org.whitesource.agent.api.dispatch.UpdateInventoryResult;
29 import org.whitesource.agent.api.model.AgentProjectInfo;
30 import org.whitesource.agent.api.model.DependencyInfo;
31 import org.whitesource.agent.client.WhitesourceService;
32 import org.whitesource.agent.client.WssServiceException;
33 import org.whitesource.agent.report.PolicyCheckReport;
34 import org.whitesource.teamcity.common.Constants;
35 import org.whitesource.teamcity.common.WssUtils;
36
37 import java.io.File;
38 import java.io.IOException;
39 import java.util.Collection;
40 import java.util.HashMap;
41 import java.util.Map;
42
43
44
45
46 public class WhitesourceLifeCycleListener extends AgentLifeCycleAdapter {
47
48
49
50 private static final String LOG_COMPONENT = "LifeCycleListener";
51
52 private ExtensionHolder extensionHolder;
53
54
55
56
57
58
59
60
61
62 public WhitesourceLifeCycleListener(@NotNull final EventDispatcher<AgentLifeCycleListener> eventDispatcher,
63 @NotNull final ExtensionHolder extensionHolder) {
64 this.extensionHolder = extensionHolder;
65 eventDispatcher.addListener(this);
66 }
67
68 @Override
69 public void agentInitialized(@NotNull BuildAgent agent) {
70 super.agentInitialized(agent);
71 Loggers.AGENT.info(WssUtils.logMsg(LOG_COMPONENT, "initialized"));
72 }
73
74
75
76 @Override
77 public void beforeRunnerStart(@NotNull BuildRunnerContext runner) {
78 super.beforeRunnerStart(runner);
79
80 if (shouldUpdate(runner)) {
81 Loggers.AGENT.info(WssUtils.logMsg(LOG_COMPONENT, "before runner start "
82 + runner.getBuild().getProjectName() + " type " + runner.getName()));
83 }
84 }
85
86 @Override
87 public void runnerFinished(@NotNull BuildRunnerContext runner, @NotNull BuildFinishedStatus status) {
88 super.runnerFinished(runner, status);
89
90 AgentRunningBuild build = runner.getBuild();
91
92 Loggers.AGENT.info(WssUtils.logMsg(LOG_COMPONENT, "runner finished "
93 + build.getProjectName() + " type " + runner.getName()));
94
95 if (!shouldUpdate(runner)) {
96 return;
97 }
98
99 final BuildProgressLogger buildLogger = build.getBuildLogger();
100 buildLogger.message("Updating White Source");
101
102
103 Map<String, String> runnerParameters = runner.getRunnerParameters();
104 String orgToken = runnerParameters.get(Constants.RUNNER_OVERRIDE_ORGANIZATION_TOKEN);
105 if (StringUtil.isEmptyOrSpaces(orgToken)) {
106 orgToken = runnerParameters.get(Constants.RUNNER_ORGANIZATION_TOKEN);
107 }
108 if (StringUtil.isEmptyOrSpaces(orgToken)) {
109 stopBuildOnError((AgentRunningBuildEx) build,
110 new IllegalStateException("Empty organization token. " +
111 "Please make sure an organization token is defined for this runner"));
112 return;
113 }
114
115
116 boolean shouldCheckPolicies = false;
117 String overrideCheckPolicies = runnerParameters.get(Constants.RUNNER_OVERRIDE_CHECK_POLICIES);
118 if (StringUtils.isBlank(overrideCheckPolicies) ||
119 "global".equals(overrideCheckPolicies)) {
120 shouldCheckPolicies = Boolean.parseBoolean(runnerParameters.get(Constants.RUNNER_CHECK_POLICIES));
121 } else {
122 shouldCheckPolicies = "enabled".equals(overrideCheckPolicies);
123 }
124
125
126 buildLogger.message("Collecting OSS usage information");
127 BaseOssInfoExtractor extractor = null;
128 if (WssUtils.isMavenRunType(runner.getRunType())) {
129 extractor = new MavenOssInfoExtractor(runner);
130 } else {
131 extractor = new GenericOssInfoExtractor(runner);
132 }
133 Collection<AgentProjectInfo> projectInfos = extractor.extract();
134 debugAgentProjectInfos(projectInfos);
135
136
137 if (CollectionUtils.isEmpty(projectInfos)) {
138 buildLogger.message("No open source information found.");
139 } else {
140 WhitesourceService service = createServiceClient(runner);
141 try{
142 if (shouldCheckPolicies) {
143 buildLogger.message("Checking policies");
144 CheckPoliciesResult result = service.checkPolicies(orgToken, projectInfos);
145 policyCheckReport(runner, result);
146 if (result.hasRejections()) {
147 stopBuild((AgentRunningBuildEx) build, "Open source rejected by organization policies.");
148 } else {
149 buildLogger.message("All dependencies conform with open source policies.");
150 sendUpdate(orgToken, projectInfos, service, buildLogger);
151 }
152 } else {
153 sendUpdate(orgToken, projectInfos, service, buildLogger);
154 }
155 } catch (WssServiceException e) {
156 stopBuildOnError((AgentRunningBuildEx) build, e);
157 } catch (IOException e) {
158 stopBuildOnError((AgentRunningBuildEx) build, e);
159 } catch (RuntimeException e) {
160 Loggers.AGENT.error(WssUtils.logMsg(LOG_COMPONENT, "Runtime Error"), e);
161 stopBuildOnError((AgentRunningBuildEx) build, e);
162 } finally {
163 service.shutdown();
164 }
165 }
166 }
167
168 private void policyCheckReport(BuildRunnerContext runner, CheckPoliciesResult result) throws IOException {
169 AgentRunningBuild build = runner.getBuild();
170
171 PolicyCheckReport report = new PolicyCheckReport(result, build.getProjectName(), build.getBuildNumber());
172 File reportArchive = report.generate(build.getBuildTempDirectory(), true);
173
174 ArtifactsPublisher publisher = extensionHolder.getExtensions(ArtifactsPublisher.class).iterator().next();
175 Map<File, String> artifactsToPublish = new HashMap<File, String>();
176 artifactsToPublish.put(reportArchive, "");
177 publisher.publishFiles(artifactsToPublish);
178 }
179
180
181
182 private boolean shouldUpdate(BuildRunnerContext runner) {
183 String shouldUpdate = runner.getRunnerParameters().get(Constants.RUNNER_DO_UPDATE);
184 return !StringUtil.isEmptyOrSpaces(shouldUpdate) && Boolean.parseBoolean(shouldUpdate);
185 }
186
187 private WhitesourceService createServiceClient(BuildRunnerContext runner) {
188 String serviceUrl = runner.getRunnerParameters().get(Constants.RUNNER_SERVICE_URL);
189 WhitesourceService service = new WhitesourceService(Constants.AGENT_TYPE, Constants.AGENT_VERSION, serviceUrl);
190
191 String proxyHost = runner.getRunnerParameters().get(Constants.RUNNER_PROXY_HOST);
192 if (!StringUtil.isEmptyOrSpaces(proxyHost)) {
193 int port = Integer.parseInt(runner.getRunnerParameters().get(Constants.RUNNER_PROXY_PORT));
194 String username = runner.getRunnerParameters().get(Constants.RUNNER_PROXY_USERNAME);
195 String password = runner.getRunnerParameters().get(Constants.RUNNER_PROXY_PASSWORD);
196 service.getClient().setProxy(proxyHost, port, username, password);
197 }
198
199 return service;
200 }
201
202 private void sendUpdate(String orgToken, Collection<AgentProjectInfo> projectInfos,
203 WhitesourceService service, BuildProgressLogger buildLogger)
204 throws WssServiceException {
205
206 buildLogger.message("Sending to White Source");
207 UpdateInventoryResult updateResult = service.update(orgToken, projectInfos);
208 logUpdateResult(updateResult, buildLogger);
209 }
210
211 private void logUpdateResult(UpdateInventoryResult result, BuildProgressLogger logger) {
212 Loggers.AGENT.info(WssUtils.logMsg(LOG_COMPONENT, "update success"));
213
214 logger.message("White Source update results: ");
215 logger.message("White Source organization: " + result.getOrganization());
216 logger.message(result.getCreatedProjects().size() + " Newly created projects:");
217 logger.message(StringUtil.join(result.getCreatedProjects(), ","));
218 logger.message(result.getUpdatedProjects().size() + " existing projects were updated:");
219 logger.message(StringUtil.join(result.getUpdatedProjects(), ","));
220 }
221
222 private void stopBuildOnError(AgentRunningBuildEx build, Exception e) {
223 Loggers.AGENT.warn(WssUtils.logMsg(LOG_COMPONENT, "Stopping build"), e);
224
225 BuildProgressLogger logger = build.getBuildLogger();
226 String errorMessage = e.getLocalizedMessage();
227 logger.buildFailureDescription(errorMessage);
228 logger.exception(e);
229 logger.flush();
230 build.stopBuild(errorMessage);
231 }
232
233 private void stopBuild(AgentRunningBuildEx build, String message) {
234 Loggers.AGENT.warn(WssUtils.logMsg(LOG_COMPONENT, "Stopping build: + message"));
235
236 BuildProgressLogger logger = build.getBuildLogger();
237 logger.buildFailureDescription(message);
238 logger.flush();
239 build.stopBuild(message);
240 }
241
242 private void debugAgentProjectInfos(Collection<AgentProjectInfo> projectInfos) {
243 final Logger log = Loggers.AGENT;
244
245 log.info("----------------- dumping projectInfos -----------------");
246 log.info("Total number of projects : " + projectInfos.size());
247 for (AgentProjectInfo projectInfo : projectInfos) {
248 log.info("Project coordiantes: " + projectInfo.getCoordinates());
249 log.info("Project parent coordiantes: " + projectInfo.getParentCoordinates());
250
251 Collection<DependencyInfo> dependencies = projectInfo.getDependencies();
252 log.info("total # of dependencies: " + dependencies.size());
253 for (DependencyInfo info : dependencies) {
254 log.info(info + " SHA-1: " + info.getSha1());
255 }
256 }
257 log.info("----------------- dump finished -----------------");
258
259 }
260 }