ch.cern.dss.teamcity.agent.MockCallable.java Source code

Java tutorial

Introduction

Here is the source code for ch.cern.dss.teamcity.agent.MockCallable.java

Source

/**
 * Copyright (c) 2012-2013 by European Organization for Nuclear Research (CERN)
 * Author: Justin Salmon <jsalmon@cern.ch>
 *
 * This file is part of the Mock TeamCity plugin.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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 Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package ch.cern.dss.teamcity.agent;

import ch.cern.dss.teamcity.common.MockConstants;
import ch.cern.dss.teamcity.common.SystemCommandResult;
import ch.cern.dss.teamcity.common.Util;
import com.intellij.openapi.util.text.StringUtil;
import jetbrains.buildServer.RunBuildException;
import jetbrains.buildServer.agent.BuildFinishedStatus;
import jetbrains.buildServer.agent.BuildProgressLogger;
import jetbrains.buildServer.util.FileUtil;
import org.apache.commons.io.FileUtils;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;

/**
 * Callable (thread) implementation in which to run a single mock build.
 */
public class MockCallable implements Callable<BuildFinishedStatus> {

    private final MockContext context;
    private final BuildProgressLogger logger;

    /**
     * @param context the context utility class.
     * @param logger  the build progress logger.
     */
    public MockCallable(@NotNull MockContext context, @NotNull BuildProgressLogger logger) {
        this.context = context;
        this.logger = logger;
    }

    /**
     * Thread entry point.
     *
     * @return enum constant representing the exit state of this thread.
     * @throws RunBuildException
     */
    @Override
    public BuildFinishedStatus call() throws RunBuildException {

        // Initialize the chroot environment, if necessary.
        try {

            if (!new File(MockConstants.MOCK_CHROOT_DIR, context.getChrootName()).exists()) {
                initializeChrootEnvironment();
            }

            clean();
            rebuild();
            publishResults();
            publishManifest();

        } catch (Exception e) {
            logger.exception(e);
            return BuildFinishedStatus.FINISHED_FAILED;
        }

        return BuildFinishedStatus.FINISHED_SUCCESS;
    }

    /**
     * Initialize an individual chroot environment with mock.
     *
     * @throws RunBuildException
     */
    private void initializeChrootEnvironment() throws RunBuildException {
        logger.message("Initializing mock environment: " + context.getChrootName());

        String[] command = { MockConstants.MOCK_EXECUTABLE, "--init", "-r", context.getChrootName(),
                "--configdir=" + context.getMockConfigDirectory() };
        SystemCommandResult result;

        try {
            result = Util.runSystemCommand(command);
        } catch (Exception e) {
            throw new RunBuildException("Unable to initialize mock environment", e);
        }

        if (result.getReturnCode() != 0) {
            throw new RunBuildException("Unable to initialize mock environment: " + result.getOutput());
        }
    }

    /**
     * Cleanup old build results.
     *
     * @throws IOException
     */
    private void clean() throws IOException {
        FileUtils.deleteDirectory(new File(MockConstants.MOCK_CHROOT_DIR, context.getChrootName() + "/result"));
    }

    /**
     * Run the actual mock build.
     *
     * @throws RunBuildException
     */
    private void rebuild() throws RunBuildException {
        String[] command = { MockConstants.MOCK_EXECUTABLE, "--rebuild", "-r", context.getChrootName(),
                "--configdir=" + context.getMockConfigDirectory(), StringUtil.join(context.getSrpms(), " ") };

        // Append RPM macros if we have any
        if (context.getRpmMacros() != null) {
            command = Util.concatArrays(command, processRpmMacros(context.getRpmMacros()));
        }

        SystemCommandResult result;
        logger.message("Running mock: " + Arrays.toString(command));

        try {
            result = Util.runSystemCommand(command);
        } catch (Exception e) {
            throw new RunBuildException("Error running mock", e);
        }

        if (result.getReturnCode() != 0) {
            logger.warning("Mock exited with nozero code (" + result.getReturnCode() + "): " + result.getOutput());
        }

        // Check if rpms were created
        File rpmDirectory = new File(MockConstants.MOCK_CHROOT_DIR, context.getChrootName() + "/result");
        File[] files = rpmDirectory.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File directory, String name) {
                return name.endsWith(".rpm");
            }
        });

        if (files.length <= 0)
            throw new RunBuildException("Error running mock: RPMs not created");
    }

    /**
     * Process each user-defined RPM macro into a command-line ready state.
     *
     * @param macros the user-defined macro string.
     *
     * @return the processed macros.
     */
    private String[] processRpmMacros(String macros) {
        List<String> macroList = new ArrayList<String>();

        for (String entry : macros.split("--define=")) {
            if (entry.length() > 0) {
                String value = entry.replace("\n", "");
                value = value.trim();
                value = value.substring(1, value.length() - 1);
                macroList.add("--define");
                macroList.add(value);
            }
        }

        return macroList.toArray(new String[macroList.size()]);
    }

    /**
     * Copy the build results and the logs to the artifacts directory.
     *
     * @throws IOException
     */
    private void publishResults() throws IOException {
        // Make sure the results (artifacts) directory exists
        File artifactsDirectory = new File(context.getArtifactsPath());
        if (!artifactsDirectory.exists()) {
            artifactsDirectory.mkdirs();
        }

        // Create the destination directory if it doesn't exist
        File destinationDirectory = new File(artifactsDirectory, context.getChrootName());
        if (!destinationDirectory.exists()) {
            destinationDirectory.mkdirs();
        }

        // Create the log directory if it doesn't exist
        File logDirectory = new File(destinationDirectory, "logs");
        if (!logDirectory.exists()) {
            logDirectory.mkdirs();
        }

        // Move the results to the artifact directory
        File sourceDirectory = new File(MockConstants.MOCK_CHROOT_DIR, context.getChrootName() + "/result");
        for (File file : sourceDirectory.listFiles()) {
            if (file.getName().endsWith(".log")) {
                FileUtils.copyFile(file, new File(logDirectory, file.getName()));
            }

            if (file.getName().endsWith(".rpm")) {
                FileUtils.copyFile(file, new File(destinationDirectory, file.getName()));
            }
        }
    }

    /**
     * Write a metadata file inside the chroot result directory, for use by the abi-checker plugin.
     *
     * @throws IOException
     */
    private void publishManifest() throws IOException {
        File manifestFile = new File(context.getArtifactsPath(), context.getChrootName() + "/manifest.txt");
        if (!manifestFile.exists()) {
            manifestFile.createNewFile();
        }

        // Simply write the chroot name for now
        FileUtil.writeFile(manifestFile, "chroot=" + context.getChrootName(), "UTF-8");
    }
}