com.xebialabs.deployit.ci.server.DeployCommand.java Source code

Java tutorial

Introduction

Here is the source code for com.xebialabs.deployit.ci.server.DeployCommand.java

Source

/**
 * Copyright (c) 2014, XebiaLabs B.V., All rights reserved.
 *
 *
 * The XL Deploy plugin for Jenkins is licensed under the terms of the GPLv2
 * <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most XebiaLabs Libraries.
 * There are special exceptions to the terms and conditions of the GPLv2 as it is applied to
 * this software, see the FLOSS License Exception
 * <https://github.com/jenkinsci/deployit-plugin/blob/master/LICENSE>.
 *
 * This program is free software; you can redistribute it and/or modify it under the terms
 * of the GNU General Public License as published by the Free Software Foundation; version 2
 * of the License.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this
 * program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth
 * Floor, Boston, MA 02110-1301  USA
 */

package com.xebialabs.deployit.ci.server;

import java.text.SimpleDateFormat;
import java.util.GregorianCalendar;
import java.util.List;

import org.apache.commons.lang.StringUtils;

import com.google.common.base.Throwables;

import com.xebialabs.deployit.ci.DeployitPluginException;
import com.xebialabs.deployit.ci.JenkinsDeploymentOptions;
import com.xebialabs.deployit.ci.util.JenkinsDeploymentListener;
import com.xebialabs.deployit.engine.api.DeploymentService;
import com.xebialabs.deployit.engine.api.RepositoryService;
import com.xebialabs.deployit.engine.api.TaskService;
import com.xebialabs.deployit.engine.api.dto.Deployment;
import com.xebialabs.deployit.engine.api.dto.ValidatedConfigurationItem;
import com.xebialabs.deployit.engine.api.execution.StepExecutionState;
import com.xebialabs.deployit.engine.api.execution.StepState;
import com.xebialabs.deployit.engine.api.execution.TaskExecutionState;
import com.xebialabs.deployit.engine.api.execution.TaskState;
import com.xebialabs.deployit.plugin.api.reflect.Type;
import com.xebialabs.deployit.plugin.api.udm.ConfigurationItem;
import com.xebialabs.deployit.plugin.api.validation.ValidationMessage;

import static com.google.common.collect.Lists.newArrayList;
import static java.lang.String.format;

public class DeployCommand {

    private DeploymentService deploymentService;
    private TaskService taskService;
    private JenkinsDeploymentOptions deploymentOptions;
    private JenkinsDeploymentListener listener;
    private RepositoryService repositoryService;

    DeployCommand(DeploymentService deploymentService, TaskService taskService, RepositoryService repositoryService,
            JenkinsDeploymentOptions deploymentOptions, JenkinsDeploymentListener listener) {
        this.deploymentService = deploymentService;
        this.taskService = taskService;
        this.repositoryService = repositoryService;
        this.deploymentOptions = deploymentOptions;
        this.listener = listener;
    }

    private void verifyPackageExistInRemoteRepository(String deploymentPackage) {
        boolean found = false;
        Type foundType = null;
        try {
            ConfigurationItem repoPackage = repositoryService.read(deploymentPackage);
            foundType = repoPackage.getType();
            listener.debug(String.format("Found CI '%s' as '%s' .", repoPackage, foundType));
        } catch (Throwable t) {
            String errorMsg = String.format("'%s' not found in repository.", deploymentPackage);
            throw new DeployitPluginException(errorMsg, t);
        }

        Type versionType = Type.valueOf("udm.Version");
        if (!foundType.isSubTypeOf(versionType)) {
            String errorMsg = String.format(
                    "'%s' of type '%s' is not a deployment package. Please verify that 'Version' is specified in Jenkins configuration.",
                    deploymentPackage, foundType);
            throw new DeployitPluginException(errorMsg);
        }

    }

    public void deploy(String deploymentPackage, String environment) {
        listener.debug(deploymentOptions.toString());

        verifyPackageExistInRemoteRepository(deploymentPackage);
        boolean initialDeployment = !deploymentService
                .isDeployed(DeployitServerFactory.getParentId(deploymentPackage), environment);

        Deployment deployment;
        if (initialDeployment) {
            listener.info("initial Deployment");
            deployment = deploymentService.prepareInitial(deploymentPackage, environment);
        } else {
            listener.info("upgrade Deployment");
            String deployedApplicationId = environment + "/"
                    + DeployitServerFactory.getNameFromId(DeployitServerFactory.getParentId(deploymentPackage));
            deployment = deploymentService.prepareUpdate(deploymentPackage, deployedApplicationId);
        }

        if (deploymentOptions.generateDeployedOnUpgrade) {
            listener.debug("prepareAutoDeployeds");
            deployment = deploymentService.prepareAutoDeployeds(deployment);
        }

        listener.debug(" dump Deployeds");
        for (ConfigurationItem itemDto : deployment.getDeployeds()) {
            listener.debug(" - " + itemDto);
        }

        try {
            listener.debug("validate");
            deployment = deploymentService.validate(deployment);
        } catch (RuntimeException e) {
            listener.error(" RuntimeException: " + e.getMessage());
            if (!e.getMessage().contains("The task did not deliver any steps")) {
                throw new DeployitPluginException(e.getMessage(), e);
            }
            return;
        }

        int validationMessagesFound = 0;
        for (ConfigurationItem configurationItem : deployment.getDeployeds()) {
            if (!(configurationItem instanceof ValidatedConfigurationItem)) {
                continue;
            }
            for (ValidationMessage msg : ((ValidatedConfigurationItem) configurationItem).getValidations()) {
                listener.error(
                        String.format("Validation error found on item '%s' of type '%s' on field '%s': %s, %s",
                                configurationItem.getId(), configurationItem.getType(), msg.getCiId(),
                                msg.getMessage(), configurationItem));
                listener.error(String.format(" %s", configurationItem));
                validationMessagesFound++;
            }
        }

        if (validationMessagesFound > 0) {
            throw new DeployitPluginException(String.format(
                    "Validation errors (%d) have been found. For more information previously reported ERROR messages.",
                    validationMessagesFound));
        }

        listener.debug("deploy");

        String taskId = deploymentService.createTask(deployment);

        try {
            executeTask(taskId);
        } catch (RuntimeException e) {
            try {
                if (deploymentOptions.rollbackOnError) {
                    // perform a rollback
                    listener.error("Deployment failed, performing a rollback");
                    executeTask(deploymentService.rollback(taskId));
                }
            } finally {
                throw new DeployitPluginException(e.getMessage(), e);
            }
        }
    }

    private boolean executeTask(String taskId) {

        if (deploymentOptions.skipMode) {
            listener.info("skip mode, skip all the steps");
            taskService.skip(taskId, range(taskService.getTask(taskId).getNrSteps() + 1));
        }

        checkTaskState(taskId);
        if (deploymentOptions.testMode) {
            listener.info("test mode, cancel task " + taskId);
            taskService.cancel(taskId);
            return false;
        }

        try {
            listener.info("Start deployment task " + taskId);
            startTaskAndWait(taskId);
            checkTaskState(taskId);
            taskService.archive(taskId);
            return true;
        } catch (RuntimeException e) {
            String msg = format("Error when executing task %s: %s", taskId, e.getMessage());
            listener.error(msg);
            throw new DeployitPluginException(msg, e);
        }

    }

    private List<Integer> range(int end) {
        List<Integer> result = newArrayList();
        for (int i = 1; i < end; i++) {
            result.add(i);
        }
        return result;
    }

    private void checkTaskState(String taskId) {
        TaskState taskState = taskService.getTask(taskId);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss");
        listener.info(format("%s Description   %s", taskId, taskState.getDescription()));
        listener.info(format("%s State         %s %d/%d", taskId, taskState.getState(),
                taskState.getCurrentStepNr(), taskState.getNrSteps()));
        if (taskState.getStartDate() != null) {
            final GregorianCalendar startDate = taskState.getStartDate().toGregorianCalendar();
            listener.info(format("%s Start      %s", taskId, sdf.format(startDate.getTime())));
        }

        if (taskState.getCompletionDate() != null) {
            final GregorianCalendar completionDate = taskState.getCompletionDate().toGregorianCalendar();
            listener.info(format("%s Completion %s", taskId, sdf.format(completionDate.getTime())));
        }

        StringBuilder sb = new StringBuilder();
        for (int i = 1; i <= taskState.getNrSteps(); i++) {
            final StepState stepInfo = taskService.getStep(taskId, i, null);
            final String description = stepInfo.getDescription();
            final String log = stepInfo.getLog();
            String stepInfoMessage;
            if (StringUtils.isEmpty(log) || description.equals(log)) {
                stepInfoMessage = format("%s step #%d %s\t%s", taskId, i, stepInfo.getState(), description);
            } else {
                stepInfoMessage = format("%s step #%d %s\t%s\n%s", taskId, i, stepInfo.getState(), description,
                        log);
            }

            listener.info(stepInfoMessage);
            if (StepExecutionState.FAILED.equals(stepInfo.getState()))
                sb.append(stepInfoMessage);
        }

        if (taskState.getState().isExecutionHalted())
            throw new DeployitPluginException(format("Errors when executing task %s: %s", taskId, sb));
    }

    private void startTaskAndWait(String taskId) {
        taskService.start(taskId);
        // Wait until done/failed
        boolean done = false;
        TaskState ti;

        int retryCount = 1;
        while (!done) {
            try {
                ti = taskService.getTask(taskId);
                TaskExecutionState state = ti.getState();
                listener.debug("Task state: " + state.toString());
                done = state.isPassiveAfterExecuting();
                retryCount = 1;
            } catch (Exception e) {
                if (retryCount == 6) { //fail after 5 consecutive errors.
                    Throwables.propagate(e);
                } else {
                    listener.info(
                            "Failed to get task status. Error message: " + Throwables.getRootCause(e).getMessage());
                    listener.info("Will attempt retry " + retryCount + " of 5 in one second.");
                    retryCount++;
                }
            }

            try {
                listener.debug("Waiting for task to be done...");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}