All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jboss.pnc.managers.ProductMilestoneReleaseManager Maven / Gradle / Ivy

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.bpm.BpmEventType;
import org.jboss.pnc.bpm.BpmManager;
import org.jboss.pnc.bpm.BpmTask;
import org.jboss.pnc.bpm.task.MilestoneReleaseTask;
import org.jboss.pnc.model.Artifact;
import org.jboss.pnc.model.BuildConfigurationAudited;
import org.jboss.pnc.model.BuildRecord;
import org.jboss.pnc.model.BuildRecordPushResult;
import org.jboss.pnc.model.MilestoneReleaseStatus;
import org.jboss.pnc.model.ProductMilestone;
import org.jboss.pnc.model.ProductMilestoneRelease;
import org.jboss.pnc.model.ProductVersion;
import org.jboss.pnc.rest.restmodel.bpm.BpmNotificationRest;
import org.jboss.pnc.rest.restmodel.bpm.BpmStringMapNotificationRest;
import org.jboss.pnc.rest.restmodel.causeway.ArtifactImportError;
import org.jboss.pnc.rest.restmodel.causeway.BuildImportResultRest;
import org.jboss.pnc.rest.restmodel.causeway.BuildImportStatus;
import org.jboss.pnc.rest.restmodel.causeway.MilestoneReleaseResultRest;
import org.jboss.pnc.spi.datastore.repositories.ArtifactRepository;
import org.jboss.pnc.spi.datastore.repositories.BuildRecordPushResultRepository;
import org.jboss.pnc.spi.datastore.repositories.BuildRecordRepository;
import org.jboss.pnc.spi.datastore.repositories.ProductMilestoneReleaseRepository;
import org.jboss.pnc.spi.datastore.repositories.ProductMilestoneRepository;
import org.jboss.pnc.spi.datastore.repositories.ProductVersionRepository;
import org.jboss.pnc.spi.exception.CoreException;
import org.jboss.pnc.spi.exception.ProcessManagerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ejb.Stateless;
import javax.inject.Inject;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Function;

import static org.jboss.pnc.common.util.CollectionUtils.ofNullableCollection;

/**
 * Author: Michal Szynkiewicz, [email protected]
 * Date: 8/31/16
 * Time: 8:42 AM
 */
@Stateless
public class ProductMilestoneReleaseManager {

    private static final Logger log = LoggerFactory.getLogger(ProductMilestoneReleaseManager.class);
    public static final String BREW_ID = "brewId";
    public static final String BREW_LINK = "brewLink";

    private BpmManager bpmManager;

    private ArtifactRepository artifactRepository;
    private ProductVersionRepository productVersionRepository;
    private BuildRecordRepository buildRecordRepository;
    private ProductMilestoneReleaseRepository productMilestoneReleaseRepository;
    private ProductMilestoneRepository milestoneRepository;
    private BuildRecordPushResultRepository buildRecordPushResultRepository;

    @Deprecated // for ejb
    public ProductMilestoneReleaseManager() {
    }

    @Inject
    public ProductMilestoneReleaseManager(
            ProductMilestoneReleaseRepository productMilestoneReleaseRepository,
            BpmManager bpmManager,
            ArtifactRepository artifactRepository,
            ProductVersionRepository productVersionRepository,
            BuildRecordRepository buildRecordRepository,
            ProductMilestoneRepository milestoneRepository,
            BuildRecordPushResultRepository buildRecordPushResultRepository) {
        this.productMilestoneReleaseRepository = productMilestoneReleaseRepository;
        this.bpmManager = bpmManager;
        this.artifactRepository = artifactRepository;
        this.productVersionRepository = productVersionRepository;
        this.buildRecordRepository = buildRecordRepository;
        this.milestoneRepository = milestoneRepository;
        this.buildRecordPushResultRepository = buildRecordPushResultRepository;
    }

    /**
     * Starts milestone release process
     *
     * @param milestone product milestone to start the release for
     * @param accessToken
     */
    public void startRelease(ProductMilestone milestone, String accessToken) {
        ProductMilestoneRelease release = triggerRelease(milestone, accessToken);
        productMilestoneReleaseRepository.save(release);
    }

    public void cancel(ProductMilestone milestoneInDb) {
        Collection activeTasks = bpmManager.getActiveTasks();
        Optional milestoneReleaseTask = activeTasks.stream()
                .map(task -> (MilestoneReleaseTask) task)
                .filter(task -> task.getMilestone().getId().equals(milestoneInDb.getId()))
                .findAny();

        if (milestoneReleaseTask.isPresent()) {
            bpmManager.cancelTask(milestoneReleaseTask.get());
        }

        ProductMilestoneRelease milestoneRelease = productMilestoneReleaseRepository.findLatestByMilestone(milestoneInDb);
        milestoneRelease.setStatus(MilestoneReleaseStatus.CANCELED);
        productMilestoneReleaseRepository.save(milestoneRelease);
    }

    public boolean noReleaseInProgress(ProductMilestone milestone) {
        ProductMilestoneRelease latestRelease = productMilestoneReleaseRepository.findLatestByMilestone(milestone);

        return latestRelease == null || latestRelease.getStatus() != MilestoneReleaseStatus.IN_PROGRESS;
    }

    private  ProductMilestoneRelease triggerRelease(ProductMilestone milestone, String accessToken) {
        ProductMilestoneRelease release = new ProductMilestoneRelease();
        release.setStartingDate(new Date());
        release.setMilestone(milestone);
        try {
            MilestoneReleaseTask releaseTask = new MilestoneReleaseTask(milestone, accessToken);
            Integer id = milestone.getId();
            releaseTask.addListener(BpmEventType.BREW_IMPORT_SUCCESS, r -> onSuccessfulPush(id, r));
            releaseTask.addListener(BpmEventType.BREW_IMPORT_ERROR, r -> onFailedPush(milestone.getId(), r));
            release.setStatus(MilestoneReleaseStatus.IN_PROGRESS);
            bpmManager.startTask(releaseTask);
            release.setLog("Brew push task started\n");

            return release;
        } catch (CoreException e) {
            log.error("Error trying to start brew push task for milestone: {}", milestone.getId(), e);
            release.setLog("Brew push BPM task creation failed.\nCheck log for more details.\n");
            release.setStatus(MilestoneReleaseStatus.SYSTEM_ERROR);
            release.setEndDate(new Date());
            return release;
        }
    }

    private void onSuccessfulPush(Integer milestoneId, MilestoneReleaseResultRest result) {
        log.debug("Storing milestone release result: {}", result);
        withMilestone(milestoneId, result, this::storeSuccess);
    }

    private void onFailedPush(Integer milestoneId, BpmStringMapNotificationRest result) {
        log.debug("Storing failed milestone release result: {}", result);
        withMilestone(milestoneId, result, this::storeFailure);
    }

    private  void withMilestone(Integer milestoneId, T result, BiConsumer consumer) {
        ProductMilestone milestone = milestoneRepository.queryById(milestoneId);

        if (milestone == null) {
            log.error("No milestone found for milestone id {}", milestoneId);
            return;
        }
        consumer.accept(milestone, result);
    }

    private void storeSuccess(ProductMilestone milestone, MilestoneReleaseResultRest result) {
        String message = describeCompletedPush(result);
        updateRelease(milestone, message, result.getReleaseStatus().getMilestoneReleaseStatus());

        for (BuildImportResultRest buildRest : ofNullableCollection(result.getBuilds())) {
            storeBrewBuildParameters(buildRest);
        }

        if (result.getReleaseStatus().getMilestoneReleaseStatus() == MilestoneReleaseStatus.SUCCEEDED) {
            // set milestone end date to now when the release process is successful
            milestone.setEndDate(new Date());
            milestoneRepository.save(milestone);

            removeCurrentFlagFromMilestone(milestone);
        }
    }

    private  void storeFailure(ProductMilestone milestone, BpmStringMapNotificationRest result) {
        updateRelease(milestone, "BREW IMPORT FAILED\nResult: " + result, MilestoneReleaseStatus.SYSTEM_ERROR);
    }

    private void storeBrewBuildParameters(BuildImportResultRest buildRest) {
        Integer recordId = buildRest.getBuildRecordId();
        BuildRecord record = buildRecordRepository.queryById(recordId);
        if (record == null) {
            log.error("No record found for record id: {}, skipped saving info: {}", recordId, buildRest);
            return;
        }

        String combinedLog = ArtifactImportError.combineMessages(buildRest.getErrorMessage(), buildRest.getErrors());


        BuildRecordPushResult.Status status;
        try {
            status = convertStatus(buildRest.getStatus());
        } catch (ProcessManagerException e) {
            log.error("Cannot convert status.", e);
            throw new RuntimeException("Cannot convert status.", e);
        }

        BuildRecordPushResult buildRecordPush = BuildRecordPushResult.newBuilder()
                .buildRecord(record)
                .status(status)
                .log(combinedLog)
                .brewBuildId(buildRest.getBrewBuildId())
                .brewBuildUrl(buildRest.getBrewBuildUrl())
                .tagPrefix("") //TODO tag!
                .build();
        buildRecordPushResultRepository.save(buildRecordPush);
    }

    private BuildRecordPushResult.Status convertStatus(BuildImportStatus status) throws ProcessManagerException {
        switch (status) {
            case SUCCESSFUL:
                return BuildRecordPushResult.Status.SUCCESS;
            case FAILED:
                return BuildRecordPushResult.Status.FAILED;
            case ERROR:
                return BuildRecordPushResult.Status.SYSTEM_ERROR;
        }
        throw new ProcessManagerException("Invalid BuildImportStatus: " + status.toString());
    }

    private void updateRelease(ProductMilestone milestone, String message, MilestoneReleaseStatus status) {
        ProductMilestoneRelease release = productMilestoneReleaseRepository.findLatestByMilestone(milestone);
        if (release == null) {
            log.error("No milestone release found for milestone {}", milestone.getId());
            return;
        }
        if (status != MilestoneReleaseStatus.IN_PROGRESS) {
            release.setEndDate(new Date());
        }
        release.setStatus(status);
        release.setLog(release.getLog() + message);
        productMilestoneReleaseRepository.save(release);
    }

    private String describeCompletedPush(MilestoneReleaseResultRest result) {
        boolean success = result.isSuccessful();
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("Brew push ").append(success ? "SUCCEEDED" : "FAILED").append("\n");
        stringBuilder.append("Import details:\n");

        String errorMessage = result.getErrorMessage();
        if (errorMessage != null) {
            stringBuilder.append(errorMessage).append("\n");
        }
        for (BuildImportResultRest buildImport : result.getBuilds()) {
            describeBuildImport(stringBuilder, buildImport);
        }

        return stringBuilder.toString();
    }

    private void describeBuildImport(StringBuilder stringBuilder, BuildImportResultRest buildImport) {
        Integer buildRecordId = buildImport.getBuildRecordId();
        BuildRecord record = orNull(buildRecordId, buildRecordRepository::queryById);
        BuildConfigurationAudited buildConfiguration = orNull(record, BuildRecord::getBuildConfigurationAudited); //TODO fix audited entity usage
        stringBuilder.append("\n-------------------------------------------------------------------------\n");
        String buildMessage =
                String.format("%s [buildRecordId: %d, built from %s revision %s tag %s] import %s. Brew build id: %d, Brew build url: %s\n",
                        orNull(buildConfiguration, BuildConfigurationAudited::getName),
                        orNull(record, BuildRecord::getId),
                        orNull(record, BuildRecord::getScmRepoURL),
                        orNull(record, BuildRecord::getScmRevision),
                        orNull(record, BuildRecord::getScmTag),
                        buildImport.getStatus(),
                        buildImport.getBrewBuildId(),
                        buildImport.getBrewBuildUrl());
        stringBuilder.append(buildMessage);
        if (buildImport.getStatus() != BuildImportStatus.SUCCESSFUL) {
            stringBuilder.append("Error message: ").append(buildImport.getErrorMessage());
            List errors = buildImport.getErrors();
            if (errors != null && !errors.isEmpty()) {
                errors.forEach(e -> describeArtifactImportError(stringBuilder, e));
            }
        }
        stringBuilder.append("\n");
    }

    private void describeArtifactImportError(StringBuilder stringBuilder, ArtifactImportError e) {
        Integer artifactId = e.getArtifactId();
        Artifact artifact = artifactRepository.queryById(artifactId);

        stringBuilder.append(
                String.format("Failed to import %s [artifactId:%d]. Error message: %s\n",
                        orNull(artifact, Artifact::getIdentifier),
                        artifactId,
                        e.getErrorMessage())
        );
    }

    /**
     * [NCL-3112] Mark the milestone provided as not current
     *
     * @param milestone ProductMilestone to not be current anymore
     */
    private void removeCurrentFlagFromMilestone(ProductMilestone milestone) {
        ProductVersion productVersion = milestone.getProductVersion();

        if (productVersion.getCurrentProductMilestone() != null &&
                productVersion.getCurrentProductMilestone().getId().equals(milestone.getId())) {

            productVersion.setCurrentProductMilestone(null);
            productVersionRepository.save(productVersion);
        }
    }

    private static  R orNull(T value, Function f) {
        return value == null ? null : f.apply(value);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy