com.thoughtworks.go.remote.work.BuildWork.java Source code

Java tutorial

Introduction

Here is the source code for com.thoughtworks.go.remote.work.BuildWork.java

Source

/*
 * Copyright 2019 ThoughtWorks, Inc.
 *
 * 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.thoughtworks.go.remote.work;

import com.thoughtworks.go.domain.*;
import com.thoughtworks.go.domain.materials.MaterialAgentFactory;
import com.thoughtworks.go.plugin.access.scm.SCMExtension;
import com.thoughtworks.go.remote.AgentIdentifier;
import com.thoughtworks.go.remote.work.artifact.ArtifactsPublisher;
import com.thoughtworks.go.server.service.AgentBuildingInfo;
import com.thoughtworks.go.server.service.AgentRuntimeInfo;
import com.thoughtworks.go.util.ProcessManager;
import com.thoughtworks.go.util.SystemEnvironment;
import com.thoughtworks.go.util.TimeProvider;
import com.thoughtworks.go.util.command.*;
import com.thoughtworks.go.work.DefaultGoPublisher;
import com.thoughtworks.go.work.GoPublisher;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Set;

import static com.thoughtworks.go.util.ExceptionUtils.bomb;
import static com.thoughtworks.go.util.ExceptionUtils.messageOf;
import static java.lang.String.format;

public class BuildWork implements Work {
    private static final Logger LOGGER = LoggerFactory.getLogger(BuildWork.class);

    private final BuildAssignment assignment;
    private final String consoleLogCharset;

    private transient DefaultGoPublisher goPublisher;
    private transient TimeProvider timeProvider;
    private transient File workingDirectory;
    private transient MaterialRevisions materialRevisions;
    private transient Builders builders;
    private ArtifactsPublisher artifactsPublisher;

    public BuildWork(BuildAssignment assignment, String consoleLogCharset) {
        this.assignment = assignment;
        this.consoleLogCharset = consoleLogCharset;
    }

    private void initialize(AgentWorkContext agentWorkContext) {
        JobIdentifier jobIdentifier = assignment.getJobIdentifier();

        this.timeProvider = new TimeProvider();
        agentWorkContext.getAgentRuntimeInfo()
                .busy(new AgentBuildingInfo(jobIdentifier.buildLocatorForDisplay(), jobIdentifier.buildLocator()));
        this.workingDirectory = assignment.getWorkingDirectory();
        this.materialRevisions = assignment.materialRevisions();
        this.goPublisher = new DefaultGoPublisher(agentWorkContext.getArtifactsManipulator(), jobIdentifier,
                agentWorkContext.getRepositoryRemote(), agentWorkContext.getAgentRuntimeInfo(), consoleLogCharset);
        this.artifactsPublisher = new ArtifactsPublisher(goPublisher, agentWorkContext.getArtifactExtension(),
                assignment.getArtifactStores(), agentWorkContext.getPluginRequestProcessorRegistry(),
                workingDirectory);
        this.builders = new Builders(assignment.getBuilders(), goPublisher, agentWorkContext.getTaskExtension(),
                agentWorkContext.getArtifactExtension(), agentWorkContext.getPluginRequestProcessorRegistry());
    }

    public void doWork(EnvironmentVariableContext environmentVariableContext, AgentWorkContext agentWorkContext) {
        initialize(agentWorkContext);
        try {
            JobResult result = build(environmentVariableContext, agentWorkContext.getAgentIdentifier(),
                    agentWorkContext.getScmExtension());
            reportCompletion(result);
        } catch (InvalidAgentException e) {
            LOGGER.error("Agent UUID changed in the middle of the build.", e);
        } catch (Exception e) {
            reportFailure(e);
        } finally {
            goPublisher.stop();
        }
    }

    private void reportFailure(Exception e) {
        try {
            goPublisher.reportErrorMessage(messageOf(e), e);
        } catch (Exception reportException) {
            LOGGER.error("Unable to report error message - %s.", messageOf(e), reportException);
        }
        reportCompletion(JobResult.Failed);
    }

    private void reportCompletion(JobResult result) {
        try {
            builders.waitForCancelTasks();
            goPublisher.reportCompleted(result);
        } catch (Exception ex) {
            LOGGER.error("New error occurred during error handling:\n"
                    + "build will be rescheduled when agent starts asking for work again", ex);
        }
    }

    private JobResult build(EnvironmentVariableContext environmentVariableContext, AgentIdentifier agentIdentifier,
            SCMExtension scmExtension) {
        if (this.goPublisher.isIgnored()) {
            goPublisher.reportJobCancelled();
            return null;
        }

        goPublisher.consumeLineWithPrefix(format("Job Started: %s\n",
                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z").format(timeProvider.currentTime())));

        prepareJob(agentIdentifier, scmExtension);

        setupEnvrionmentContext(environmentVariableContext);

        dumpEnvironmentVariables(environmentVariableContext);

        if (this.goPublisher.isIgnored()) {
            goPublisher.reportJobCancelled();
            return null;
        }

        return completeJob(buildJob(environmentVariableContext, consoleLogCharset), environmentVariableContext);
    }

    private void dumpEnvironmentVariables(EnvironmentVariableContext environmentVariableContext) {
        Set<String> processLevelEnvVariables = ProcessManager.getInstance().environmentVariableNames();
        List<String> report = environmentVariableContext.report(processLevelEnvVariables);
        ConsoleOutputStreamConsumer safeOutput = new LabeledOutputStreamConsumer(DefaultGoPublisher.PREP,
                DefaultGoPublisher.PREP_ERR, safeOutputStreamConsumer(environmentVariableContext));
        for (int i = 0; i < report.size(); i++) {
            String line = report.get(i);
            safeOutput.stdOutput((i == report.size() - 1) ? line + "\n" : line);
        }
    }

    private SafeOutputStreamConsumer safeOutputStreamConsumer(
            EnvironmentVariableContext environmentVariableContext) {
        SafeOutputStreamConsumer consumer = new SafeOutputStreamConsumer(processOutputStreamConsumer());
        consumer.addSecrets(environmentVariableContext.secrets());
        return consumer;
    }

    private void prepareJob(AgentIdentifier agentIdentifier, SCMExtension scmExtension) {
        goPublisher.reportPreparing();

        createWorkingDirectoryIfNotExist(workingDirectory);
        if (!assignment.shouldFetchMaterials()) {
            goPublisher.taggedConsumeLineWithPrefix(DefaultGoPublisher.PREP,
                    "Skipping material update since stage is configured not to fetch materials");
            return;
        }

        ConsoleOutputStreamConsumer consumer = new LabeledOutputStreamConsumer(DefaultGoPublisher.PREP,
                DefaultGoPublisher.PREP_ERR, processOutputStreamConsumer());
        MaterialAgentFactory materialAgentFactory = new MaterialAgentFactory(consumer, workingDirectory,
                agentIdentifier, scmExtension);

        materialRevisions.getMaterials().cleanUp(workingDirectory, consumer);

        goPublisher.taggedConsumeLineWithPrefix(DefaultGoPublisher.PREP, "Start to update materials.\n");

        for (MaterialRevision revision : materialRevisions.getRevisions()) {
            materialAgentFactory.createAgent(revision).prepare();
        }
    }

    private ProcessOutputStreamConsumer<GoPublisher, GoPublisher> processOutputStreamConsumer() {
        return new ProcessOutputStreamConsumer<>(goPublisher, goPublisher);
    }

    private void setupEnvrionmentContext(EnvironmentVariableContext context) {
        context.setProperty("GO_SERVER_URL", new SystemEnvironment().getPropertyImpl("serviceUrl"), false);
        context.addAll(assignment.initialEnvironmentVariableContext());
        materialRevisions.populateAgentSideEnvironmentVariables(context, workingDirectory);
    }

    private JobResult buildJob(EnvironmentVariableContext environmentVariableContext, String consoleLogCharset) {
        goPublisher.reportStartingToBuild();
        return execute(environmentVariableContext, consoleLogCharset);
    }

    private JobResult completeJob(JobResult result, EnvironmentVariableContext environmentVariableContext) {
        if (goPublisher.isIgnored()) {
            return result;
        }

        String tag = result.isPassed() ? DefaultGoPublisher.JOB_PASS : DefaultGoPublisher.JOB_FAIL;
        goPublisher.reportCompleting(result, tag);

        goPublisher.reportCreatingProperties();
        harvestProperties(goPublisher);

        goPublisher.reportBeginToPublishArtifacts();

        try {
            artifactsPublisher.publishArtifacts(assignment.getArtifactPlans(), environmentVariableContext);
        } catch (Exception e) {
            LOGGER.error(null, e);
            goPublisher.taggedConsumeLineWithPrefix(DefaultGoPublisher.PUBLISH_ERR, e.getMessage());
            return JobResult.Failed;
        }

        return result;
    }

    private JobResult execute(EnvironmentVariableContext environmentVariableContext, String consoleLogCharset) {
        JobResult result = builders.build(environmentVariableContext, consoleLogCharset);
        goPublisher.reportCompleting(result);
        return result;
    }

    private List<ArtifactPropertiesGenerator> getArtifactPropertiesGenerators() {
        return assignment.getPropertyGenerators();
    }

    private void harvestProperties(DefaultGoPublisher publisher) {
        List<ArtifactPropertiesGenerator> generators = getArtifactPropertiesGenerators();
        for (ArtifactPropertiesGenerator generator : generators) {
            generator.generate(publisher, workingDirectory);
        }
    }

    public String description() {
        return "Running build ...";
    }

    public void cancel(EnvironmentVariableContext environmentVariableContext, AgentRuntimeInfo agentruntimeInfo) {
        agentruntimeInfo.cancel();
        builders.cancel(environmentVariableContext, consoleLogCharset);
    }

    public BuildAssignment getAssignment() {
        return assignment;
    }

    public JobIdentifier identifierForLogging() {
        if (assignment == null || assignment.getJobIdentifier() == null) {
            return JobIdentifier.invalidIdentifier("Unknown", "Unknown", "Unknown", "Unknown", "Unknown");
        }
        return assignment.getJobIdentifier();
    }

    public String toString() {
        return "BuildWork[" + assignment.toString() + "]";
    }

    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        BuildWork work = (BuildWork) o;

        if (assignment != null ? !assignment.equals(work.assignment) : work.assignment != null) {
            return false;
        }

        return true;
    }

    public int hashCode() {
        int result;
        result = (assignment != null ? assignment.hashCode() : 0);
        result = 31 * result + (goPublisher != null ? goPublisher.hashCode() : 0);
        result = 31 * result + (timeProvider != null ? timeProvider.hashCode() : 0);
        return result;
    }

    private void createWorkingDirectoryIfNotExist(File buildWorkingDirectory) {
        if (assignment.shouldCleanWorkingDir() && buildWorkingDirectory.exists()) {
            try {
                FileUtils.cleanDirectory(buildWorkingDirectory);
                goPublisher.consumeLineWithPrefix(
                        "Cleaning working directory \"" + buildWorkingDirectory.getAbsolutePath()
                                + "\" since stage is configured to clean working directory");
            } catch (IOException e) {
                bomb("Clean working directory is set to true. Unable to clean working directory for agent: "
                        + buildWorkingDirectory.getAbsolutePath() + ", with error: " + e.getMessage());
            }
        }
        if (!buildWorkingDirectory.exists()) {
            if (!buildWorkingDirectory.mkdirs()) {
                bomb("Unable to create working directory for agent: " + buildWorkingDirectory.getAbsolutePath());
            }
        }
    }
}