org.jboss.pnc.managers.BuildResultPushManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of process-managers Show documentation
Show all versions of process-managers Show documentation
Handles interaction with BPM server.
The newest version!
/**
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.pnc.managers;
import org.jboss.pnc.causewayclient.CausewayClient;
import org.jboss.pnc.causewayclient.remotespi.Build;
import org.jboss.pnc.causewayclient.remotespi.BuildImportRequest;
import org.jboss.pnc.causewayclient.remotespi.BuildRoot;
import org.jboss.pnc.causewayclient.remotespi.BuiltArtifact;
import org.jboss.pnc.causewayclient.remotespi.CallbackTarget;
import org.jboss.pnc.causewayclient.remotespi.Dependency;
import org.jboss.pnc.causewayclient.remotespi.Logfile;
import org.jboss.pnc.causewayclient.remotespi.MavenBuild;
import org.jboss.pnc.causewayclient.remotespi.MavenBuiltArtifact;
import org.jboss.pnc.common.maven.Gav;
import org.jboss.pnc.model.Artifact;
import org.jboss.pnc.model.BuildConfiguration;
import org.jboss.pnc.model.BuildEnvironment;
import org.jboss.pnc.model.BuildRecord;
import org.jboss.pnc.model.BuildRecordPushResult;
import org.jboss.pnc.model.BuildStatus;
import org.jboss.pnc.model.IdRev;
import org.jboss.pnc.rest.restmodel.BuildRecordPushResultRest;
import org.jboss.pnc.spi.coordinator.ProcessException;
import org.jboss.pnc.spi.datastore.predicates.ArtifactPredicates;
import org.jboss.pnc.spi.datastore.repositories.ArtifactRepository;
import org.jboss.pnc.spi.datastore.repositories.BuildConfigurationRepository;
import org.jboss.pnc.spi.datastore.repositories.BuildRecordPushResultRepository;
import org.jboss.pnc.spi.datastore.repositories.BuildRecordRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ejb.Stateless;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author Matej Lazar
*/
@Stateless
public class BuildResultPushManager {
/**
* Generic parameter name for overriding the executionRootName value received from Repour.
*/
private static final String EXECUTION_ROOT_NAME_PARAM = "EXECUTION_ROOT_NAME";
private static final String PNC_BUILD_RECORD_PATH = "/pnc-rest/rest/build-records/%d";
private static final String PNC_BUILD_LOG_PATH = "/pnc-rest/rest/build-records/%d/log";
private static final String PNC_REPOUR_LOG_PATH = "/pnc-rest/rest/build-records/%d/repour-log";
private BuildConfigurationRepository buildConfigurationRepository;
private BuildRecordRepository buildRecordRepository;
private BuildRecordPushResultRepository buildRecordPushResultRepository;
private ArtifactRepository artifactRepository;
private InProgress inProgress;
private CausewayClient causewayClient;
private Event buildRecordPushResultRestEvent;
private Logger logger = LoggerFactory.getLogger(BuildResultPushManager.class);
@Deprecated //required by EJB
public BuildResultPushManager() {
}
@Inject
public BuildResultPushManager(BuildConfigurationRepository buildConfigurationRepository,
BuildRecordRepository buildRecordRepository,
BuildRecordPushResultRepository buildRecordPushResultRepository,
InProgress inProgress,
Event buildRecordPushResultRestEvent,
ArtifactRepository artifactRepository,
CausewayClient causewayClient
) {
this.buildConfigurationRepository = buildConfigurationRepository;
this.buildRecordRepository = buildRecordRepository;
this.buildRecordPushResultRepository = buildRecordPushResultRepository;
this.inProgress = inProgress;
this.buildRecordPushResultRestEvent = buildRecordPushResultRestEvent;
this.artifactRepository = artifactRepository;
this.causewayClient = causewayClient;
}
/**
*
* @param buildRecordIds
* @param authToken
* @param callBackUrlTemplate %d in the template will be replaced with BuildRecord.id
* @param tagPrefix
* @param reimport Wherather the build should be reimported with new revision number if it
* already exists in Brew
* @return
*/
public Set push(
Set buildRecordIds,
String authToken,
String callBackUrlTemplate, String tagPrefix,
boolean reimport) {
Set result = new HashSet<>();
for (Integer buildRecordId : buildRecordIds) {
//check is the status is NO_REBUILD_REQUIRED, if it is replace with the last BuildRecord with status SUCCESS for the same idRev.
BuildRecord buildRecord = buildRecordRepository.queryById(buildRecordId);
Integer pushBuildRecordId = null;
if (BuildStatus.NO_REBUILD_REQUIRED.equals(buildRecord.getStatus())) {
IdRev idRev = buildRecord.getBuildConfigurationAuditedIdRev();
buildRecord = buildRecordRepository.getLatestSuccessfulBuildRecord(idRev, buildRecord.isTemporaryBuild());
if (buildRecord != null) {
pushBuildRecordId = buildRecord.getId();
} else {
logger.warn("Trying to push a BuildRecord.id: {} with status NO_REBUILD_REQUIRED and there is no successful result for the configuration.idRev: {}.",
buildRecordId, idRev);
}
} else {
pushBuildRecordId = buildRecordId;
}
if (pushBuildRecordId != null) {
Result pushResult = pushToCauseway(
authToken,
pushBuildRecordId,
String.format(callBackUrlTemplate, buildRecordId),
tagPrefix,
reimport);
result.add(pushResult);
}
}
return result;
}
private Result pushToCauseway(String authToken, Integer buildRecordId, String callBackUrl, String tagPrefix, boolean reimport) {
logger.info("Pushing to causeway BR.id: {}", buildRecordId);
boolean successfullyPushed = false;
String message = "Failed to push to Causeway.";
if (!inProgress.add(buildRecordId, tagPrefix)) {
logger.warn("Push for BR.id {} already running.", buildRecordId);
return new Result(buildRecordId.toString(), Result.Status.REJECTED, "A push for this buildRecord is already running.");
}
try {
BuildRecord buildRecord = buildRecordRepository.findByIdFetchProperties(buildRecordId);
if (buildRecord == null) {
logger.warn("Did not find build record by id: " + buildRecordId);
message = "Did not find build record by given id.";
} else if (!buildRecord.getStatus().completedSuccessfully()) {
logger.warn("Not pushing record id: " + buildRecordId + " because it is a failed build.");
message = "Cannot push failed build.";
} else if (hasBadArtifactQuality(buildRecord.getBuiltArtifacts())) {
logger.warn("Not pushing record id: " + buildRecordId + " because it contains artifacts of insufficient quality: BLACKLISTED/DELETED.");
message = "Build contains artifacts of insufficient quality: BLACKLISTED/DELETED.";
}
else {
BuildImportRequest buildImportRequest = createCausewayPushRequest(buildRecord, tagPrefix, callBackUrl, authToken, reimport);
successfullyPushed = causewayClient.importBuild(buildImportRequest, authToken);
}
} catch (RuntimeException ex) {
logger.error("Failed to push to Causeway.", ex);
message = ("Failed to push to Causeway: " + ex.getMessage());
}
if (!successfullyPushed) {
inProgress.remove(buildRecordId);
return new Result(buildRecordId.toString(), Result.Status.REJECTED, message);
} else {
return new Result(buildRecordId.toString(), Result.Status.ACCEPTED);
}
}
private BuildImportRequest createCausewayPushRequest(
BuildRecord buildRecord,
String tagPrefix,
String callBackUrl,
String authToken,
boolean reimport) {
BuildEnvironment buildEnvironment = buildRecord.getBuildConfigurationAudited().getBuildEnvironment();
logger.debug("BuildRecord: {}", buildRecord.getId());
logger.debug("BuildEnvironment: {}", buildEnvironment);
BuildRoot buildRoot = new BuildRoot(
"DOCKER_IMAGE",
"x86_64", //TODO set based on env, some env has native build tools
"rhel",
"x86_64",
buildEnvironment.getAttributes()
);
List builtArtifactEntities = artifactRepository.queryWithPredicates(ArtifactPredicates.withBuildRecordId(buildRecord.getId()));
List dependencyEntities = artifactRepository.queryWithPredicates(ArtifactPredicates.withDependantBuildRecordId(buildRecord.getId()));
logger.debug("Preparing BuildImportRequest containing {} built artifacts and {} dependencies.", builtArtifactEntities.size(), dependencyEntities.size());
Set dependencies = collectDependencies(dependencyEntities);
Set builtArtifacts = collectBuiltArtifacts(builtArtifactEntities);
CallbackTarget callbackTarget = CallbackTarget.callbackPost(callBackUrl, authToken);
String executionRootName = null;
// prefer execution root name from generic parameters
BuildConfiguration bc = buildConfigurationRepository.queryById(buildRecord.getBuildConfigurationId());
Map genericParameters = bc.getGenericParameters();
if (genericParameters.containsKey(EXECUTION_ROOT_NAME_PARAM)) {
executionRootName = genericParameters.get(EXECUTION_ROOT_NAME_PARAM);
}
if (executionRootName == null) {
executionRootName = buildRecord.getExecutionRootName();
}
Gav rootGav = buildRootToGAV(
executionRootName,
buildRecord.getExecutionRootVersion());
Set logs = new HashSet<>();
addLogs(buildRecord, logs);
Build build = new MavenBuild(
rootGav.getGroupId(),
rootGav.getArtifactId(),
rootGav.getVersion(),
executionRootName,
buildRecord.getExecutionRootVersion(),
"PNC",
buildRecord.getId(),
String.format(PNC_BUILD_RECORD_PATH, buildRecord.getId()),
buildRecord.getStartTime(),
buildRecord.getEndTime(),
buildRecord.getScmRepoURL(),
buildRecord.getScmRevision(),
buildRecord.getScmTag(),
buildRoot,
logs,
dependencies,
builtArtifacts,
tagPrefix
);
return new BuildImportRequest(callbackTarget, build, reimport);
}
private void addLogs(BuildRecord buildRecord, Set logs) {
if (buildRecord.getBuildLogSize() != null && buildRecord.getBuildLogSize() > 0) {
logs.add(new Logfile("build.log", getBuildLogPath(buildRecord.getId()), buildRecord.getBuildLogSize(), buildRecord.getBuildLogMd5()));
} else {
logger.warn("Missing build log for BR.id: {}.", buildRecord.getId());
}
if (buildRecord.getRepourLogSize() != null && buildRecord.getRepourLogSize() > 0) {
logs.add(new Logfile("repour.log", getRepourLogPath(buildRecord.getId()), buildRecord.getRepourLogSize(), buildRecord.getRepourLogMd5()));
} else {
logger.warn("Missing repour log for BR.id: {}.", buildRecord.getId());
}
//TODO respond with error if logs are missing
}
private String getRepourLogPath(Integer id) {
return String.format(PNC_REPOUR_LOG_PATH, id);
}
private String getBuildLogPath(Integer id) {
return String.format(PNC_BUILD_LOG_PATH, id);
}
private Gav buildRootToGAV(String executionRootName, String executionRootVersion) {
if (executionRootName == null) {
throw new IllegalArgumentException("ExecutionRootName must be defined.");
}
String[] splittedName = executionRootName.split(":");
if (splittedName.length != 2) {
throw new IllegalArgumentException("Execution root '" + executionRootName + "' doesn't seem to be maven G:A.");
}
return new Gav(splittedName[0], splittedName[1], executionRootVersion);
}
private Set collectBuiltArtifacts(Collection builtArtifacts) {
return builtArtifacts.stream().map(artifact -> {
Gav gav = Gav.parse(artifact.getIdentifier());
return new MavenBuiltArtifact(
gav.getGroupId(),
gav.getArtifactId(),
gav.getVersion(),
artifact.getId(),
artifact.getFilename(),
artifact.getTargetRepository().getRepositoryType().toString(),
artifact.getMd5(),
artifact.getDeployPath(),
artifact.getTargetRepository().getRepositoryPath(),
artifact.getSize().intValue()
);
})
.collect(Collectors.toSet());
}
private Set collectDependencies(Collection dependencies) {
return dependencies.stream()
.map(artifact -> new Dependency(
artifact.getFilename(),
artifact.getMd5(),
artifact.getSize())
)
.collect(Collectors.toSet());
}
private boolean hasBadArtifactQuality(Collection builtArtifacts){
EnumSet badQuality = EnumSet.of(Artifact.Quality.DELETED, Artifact.Quality.BLACKLISTED);
return builtArtifacts.stream()
.map(Artifact::getArtifactQuality)
.anyMatch(badQuality::contains);
}
public Integer complete(Integer buildRecordId, BuildRecordPushResult buildRecordPushResult) throws ProcessException {
//accept only listed elements otherwise a new request might be wrongly completed from response of an older one
String completedTag = inProgress.remove(buildRecordId);
if (completedTag == null) {
throw new ProcessException("Did not find the referenced element.");
}
buildRecordPushResult.setTagPrefix(completedTag);
BuildRecordPushResult saved = buildRecordPushResultRepository.save(buildRecordPushResult);
buildRecordPushResultRestEvent.fire(new BuildRecordPushResultRest(saved));
return saved.getId();
}
public boolean cancelInProgressPush(Integer buildRecordId) {
BuildRecordPushResultRest buildRecordPushResultRest = BuildRecordPushResultRest.builder()
.status(BuildRecordPushResult.Status.CANCELED)
.buildRecordId(buildRecordId)
.log("Canceled.")
.build();
boolean canceled = inProgress.remove(buildRecordId) != null;
buildRecordPushResultRestEvent.fire(buildRecordPushResultRest);
return canceled;
}
public Set getInProgress() {
return inProgress.getAllIds();
}
}