com.dickthedeployer.dick.web.service.JobBuildService.java Source code

Java tutorial

Introduction

Here is the source code for com.dickthedeployer.dick.web.service.JobBuildService.java

Source

/*
 * Copyright dick the deployer.
 *
 * 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 com.dickthedeployer.dick.web.service;

import com.dickthedeployer.dick.web.dao.BuildDao;
import com.dickthedeployer.dick.web.dao.JobBuildDao;
import com.dickthedeployer.dick.web.dao.LogChunkDao;
import com.dickthedeployer.dick.web.dao.WorkerDao;
import com.dickthedeployer.dick.web.domain.Build;
import com.dickthedeployer.dick.web.domain.JobBuild;
import com.dickthedeployer.dick.web.domain.LogChunk;
import com.dickthedeployer.dick.web.domain.Worker;
import com.dickthedeployer.dick.web.exception.DickFileMissingException;
import com.dickthedeployer.dick.web.exception.NotFoundException;
import com.dickthedeployer.dick.web.model.BuildOrder;
import com.dickthedeployer.dick.web.model.EnvironmentVariable;
import com.dickthedeployer.dick.web.model.LogChunkModel;
import com.dickthedeployer.dick.web.model.OutputModel;
import com.dickthedeployer.dick.web.model.dickfile.Dickfile;
import com.dickthedeployer.dick.web.model.dickfile.Job;
import com.dickthedeployer.dick.web.model.dickfile.Stage;
import com.google.common.base.Throwables;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author mariusz
 */
@Slf4j
@Service
public class JobBuildService {

    @Autowired
    CommandService commandService;

    @Autowired
    WorkerService workerService;

    @Autowired
    JobBuildDao jobBuildDao;

    @Autowired
    WorkerDao workerDao;

    @Autowired
    BuildDao buildDao;

    @Autowired
    DickYmlService dickYmlService;

    @Autowired
    LogChunkDao logChunkDao;

    @Transactional
    public void prepareJobs(Build build, Dickfile dickfile) {
        dickfile.getStageNames().stream().map(dickfile::getStage)
                .forEach(stage -> dickfile.getJobs(stage).stream().map(job -> {
                    JobBuild jobBuild = new JobBuild();
                    jobBuild.setName(job.getName());
                    jobBuild.setBuild(build);
                    jobBuild.setStage(stage.getName());
                    jobBuild.setDeploy(job.getDeploy());
                    jobBuild.setStatus(JobBuild.Status.WAITING);
                    jobBuild.setDockerImage(dickfile.getImage());
                    jobBuild.setRequireRepository(job.isRequireRepository());
                    return jobBuild;
                }).forEach(jobBuildDao::save));
    }

    @Transactional
    public void buildStage(Build build, Dickfile dickfile, Stage stage) {
        List<Job> jobs = dickfile.getJobs(stage);
        build.setCurrentStage(stage.getName());
        buildDao.save(build);
        jobs.stream().forEach(job -> workerService.scheduleJobBuild(build, stage.getName(), job));

        updateBuildStatus(build);
    }

    @Transactional
    public Optional<BuildOrder> peekBuildFor(String workerName) {
        workerService.onHeartbeat(workerName);
        Worker worker = workerDao.findByName(workerName).get();
        Optional<JobBuild> jobBuildOptional = jobBuildDao.findByStatusAndWorker(JobBuild.Status.READY, worker);

        return jobBuildOptional.map(jobBuild -> prepareBuildOrder(jobBuild, worker));
    }

    public boolean isStopped(Long id) {
        JobBuild jobBuild = jobBuildDao.findOne(id);
        return jobBuild == null || jobBuild.isStopped();
    }

    @Transactional
    public void reportProgress(Long id, String partialLog) {
        JobBuild jobBuild = jobBuildDao.findOne(id);
        log.info("Reporting progress on {}", id);
        if (jobBuild != null) {
            LogChunk logChunk = new LogChunk();
            logChunk.setJobBuild(jobBuild);
            logChunk.getBuildLog().setOutput(partialLog);
            logChunkDao.save(logChunk);

            updateBuildStatus(jobBuild.getBuild());
        }
    }

    @Transactional
    public void reportFailure(Long id, String fullLog) {
        JobBuild jobBuild = jobBuildDao.findOne(id);
        log.info("Reporting failure on {}", id);
        if (jobBuild != null) {
            if (!jobBuild.getStatus().equals(JobBuild.Status.STOPPED)) {
                jobBuild.setStatus(JobBuild.Status.FAILED);
            }
            clearJob(fullLog, jobBuild);

            updateBuildStatus(jobBuild.getBuild());
        }
    }

    @Transactional
    public void reportSuccess(Long id, String fullLog) {
        JobBuild jobBuild = jobBuildDao.findOne(id);
        log.info("Reporting success on {}", id);
        if (jobBuild != null) {
            jobBuild.setStatus(JobBuild.Status.DEPLOYED);
            clearJob(fullLog, jobBuild);

            Build build = jobBuild.getBuild();
            Build.Status status = updateBuildStatus(build);
            if (status.equals(Build.Status.DEPLOYED_STAGE)) {
                log.info("Build status after job build {} is {}", id, status);
                try {
                    Dickfile dickfile = dickYmlService.loadDickFile(build);
                    Stage nextStage = dickfile.getNextStage(dickfile.getStage(build.getCurrentStage()));
                    log.info("Next stage is {}", nextStage);
                    if (nextStage == null) {
                        build.setStatus(Build.Status.DEPLOYED);
                        buildDao.save(build);
                    } else if (nextStage.isAutorun()) {
                        buildStage(build, dickfile, nextStage);
                    }
                } catch (DickFileMissingException ex) {
                    throw Throwables.propagate(ex);
                }
            }
        }
    }

    private void clearJob(String fullLog, JobBuild jobBuild) {
        workerService.readyWorker(Optional.ofNullable((jobBuild.getWorker())));
        jobBuild.setWorker(null);
        jobBuild.getBuildLog().setOutput(fullLog);
        jobBuildDao.save(jobBuild);
        logChunkDao.deleteByJobBuild(jobBuild);
    }

    public Build.Status updateBuildStatus(Build build) {
        Build.Status buildStatus = determineBuildStatus(build);
        build.setStatus(buildStatus);
        buildDao.save(build);
        return buildStatus;
    }

    private Build.Status determineBuildStatus(Build build) {
        Map<JobBuild.Status, List<JobBuild.Status>> statuses = jobBuildDao.findByBuild(build).stream()
                .map(JobBuild::getStatus).collect(Collectors.groupingBy(Function.identity()));
        if (!statuses.containsKey(JobBuild.Status.IN_PROGRESS) && !statuses.containsKey(JobBuild.Status.READY)) {
            if (statuses.containsKey(JobBuild.Status.FAILED)) {
                return Build.Status.FAILED;
            } else if (statuses.containsKey(JobBuild.Status.STOPPED)) {
                return Build.Status.STOPPED;
            } else {
                return Build.Status.DEPLOYED_STAGE;
            }
        } else if (statuses.containsKey(JobBuild.Status.IN_PROGRESS)) {
            return Build.Status.IN_PROGRESS;
        } else {
            return Build.Status.READY;
        }
    }

    private BuildOrder prepareBuildOrder(JobBuild jobBuild, Worker worker) {
        jobBuild.setStatus(JobBuild.Status.IN_PROGRESS);
        jobBuildDao.save(jobBuild);
        worker.setStatus(Worker.Status.BUSY);
        workerDao.save(worker);

        return BuildOrder.builder().buildId(jobBuild.getId()).commands(jobBuild.getDeploy())
                .environment(jobBuild.getEnvironmentVariables().stream()
                        .map(variable -> EnvironmentVariable.builder().value(variable.getVariableValue())
                                .name(variable.getVariableKey()).secure(variable.isSecure()).build())
                        .collect(Collectors.toList()))
                .requireRepository(jobBuild.isRequireRepository()).dockerImage(jobBuild.getDockerImage())
                .ref(jobBuild.getBuild().getRef()).sha(jobBuild.getBuild().getSha())
                .repository(jobBuild.getBuild().getRepository()).build();
    }

    @Transactional
    public void stop(Build build) {
        List<JobBuild> jobBuilds = jobBuildDao.findByBuild(build);
        jobBuilds.stream().filter(jobBuild -> jobBuild.getStatus().equals(JobBuild.Status.IN_PROGRESS))
                .forEach(jobBuild -> {
                    jobBuild.setStatus(JobBuild.Status.STOPPED);
                    workerService.readyWorker(Optional.of(jobBuild.getWorker()));
                    jobBuild.setWorker(null);
                    jobBuildDao.save(jobBuild);
                });
        jobBuilds.stream().filter(jobBuild -> jobBuild.getStatus().equals(JobBuild.Status.READY))
                .forEach(jobBuild -> {
                    jobBuild.setStatus(JobBuild.Status.STOPPED);
                    workerService.readyWorker(Optional.ofNullable(jobBuild.getWorker()));
                    jobBuild.setWorker(null);
                    jobBuildDao.save(jobBuild);
                });
        updateBuildStatus(build);
    }

    @Transactional
    public List<LogChunkModel> getLogChunks(Long id, Date creationDate) throws NotFoundException {
        JobBuild jobBuild = getAndCheckJobBuild(id);
        List<LogChunk> logChunks = creationDate == null ? logChunkDao.findByJobBuild(jobBuild)
                : logChunkDao.findByJobBuildAndCreationDateAfter(jobBuild, creationDate);
        return logChunks.stream().map(logChunk -> LogChunkModel.builder().output(logChunk.getBuildLog().getOutput())
                .creationDate(logChunk.getCreationDate()).build()).collect(Collectors.toList());
    }

    @Transactional
    public OutputModel getOutput(Long id) throws NotFoundException {
        JobBuild jobBuild = getAndCheckJobBuild(id);
        return OutputModel.builder().output(jobBuild.getBuildLog().getOutput()).build();
    }

    private JobBuild getAndCheckJobBuild(Long id) throws NotFoundException {
        JobBuild jobBuild = jobBuildDao.findOne(id);
        if (jobBuild == null) {
            throw new NotFoundException();
        }
        return jobBuild;
    }

    @Transactional
    public void deleteJobBuilds(Build build) {
        jobBuildDao.findByBuild(build).stream().forEach(jobBuild -> {
            logChunkDao.deleteByJobBuild(jobBuild);
            workerService.readyWorker(Optional.ofNullable(jobBuild.getWorker()));
            jobBuildDao.delete(jobBuild);
        });
    }
}