src/main/java/com/microfocus/application/automation/tools/octane/model/processors/projects/AbstractProjectProcessor.java
/*
* Certain versions of software accessible here may contain branding from Hewlett-Packard Company (now HP Inc.) and Hewlett Packard Enterprise Company.
* This software was acquired by Micro Focus on September 1, 2017, and is now offered by OpenText.
* Any reference to the HP and Hewlett Packard Enterprise/HPE marks is historical in nature, and the HP and Hewlett Packard Enterprise/HPE marks are the property of their respective owners.
* __________________________________________________________________
* MIT License
*
* Copyright 2012-2024 Open Text
*
* The only warranties for products and services of Open Text and
* its affiliates and licensors ("Open Text") are as may be set forth
* in the express warranty statements accompanying such products and services.
* Nothing herein should be construed as constituting an additional warranty.
* Open Text shall not be liable for technical or editorial errors or
* omissions contained herein. The information contained herein is subject
* to change without notice.
*
* Except as specifically indicated otherwise, this document contains
* confidential information and a valid license is required for possession,
* use or copying. If this work is provided to the U.S. Government,
* consistent with FAR 12.211 and 12.212, Commercial Computer Software,
* Computer Software Documentation, and Technical Data for Commercial Items are
* licensed to the U.S. Government under vendor's standard commercial license.
*
* 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.microfocus.application.automation.tools.octane.model.processors.projects;
import com.hp.octane.integrations.dto.DTOFactory;
import com.hp.octane.integrations.dto.general.CIBuildStatusInfo;
import com.hp.octane.integrations.dto.pipelines.PipelinePhase;
import com.hp.octane.integrations.dto.snapshots.CIBuildStatus;
import com.hp.octane.integrations.utils.SdkConstants;
import com.hp.octane.integrations.utils.SdkStringUtils;
import com.microfocus.application.automation.tools.octane.configuration.SDKBasedLoggerProvider;
import com.microfocus.application.automation.tools.octane.events.OutputEnvironmentParametersHelper;
import com.microfocus.application.automation.tools.octane.executor.UftConstants;
import com.microfocus.application.automation.tools.octane.model.processors.builders.AbstractBuilderProcessor;
import com.microfocus.application.automation.tools.octane.model.processors.builders.BuildTriggerProcessor;
import com.microfocus.application.automation.tools.octane.model.processors.builders.ParameterizedTriggerProcessor;
import com.microfocus.application.automation.tools.octane.model.processors.parameters.ParameterProcessors;
import com.microfocus.application.automation.tools.octane.tests.build.BuildHandlerUtils;
import hudson.model.*;
import hudson.tasks.Builder;
import hudson.tasks.Publisher;
import jenkins.model.Jenkins;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Created with IntelliJ IDEA.
* User: gullery
* Date: 09/01/15
* Time: 00:59
* To change this template use File | Settings | File Templates.
*/
@SuppressWarnings({"squid:S1132", "squid:S1872"})
public abstract class AbstractProjectProcessor<T extends Job> {
private static final Logger logger = SDKBasedLoggerProvider.getLogger(AbstractProjectProcessor.class);
private final List<PipelinePhase> internals = new ArrayList<>();
private final List<PipelinePhase> postBuilds = new ArrayList<>();
private boolean isProcessed = false;
T job;
AbstractProjectProcessor(T job) {
this.job = job;
}
/**
* Attempt to retrieve an [internal] build phases of the Job
*
* @return list of builders
*/
public List<Builder> tryGetBuilders() {
return new ArrayList<>();
}
/**
* Enqueue Job's run with the specified parameters
*/
public void scheduleBuild(Cause cause, ParametersAction parametersAction) {
if (job instanceof AbstractProject) {
AbstractProject project = (AbstractProject) job;
int delay = project.getQuietPeriod();
project.scheduleBuild(delay, cause, parametersAction);
} else {
throw new IllegalStateException("unsupported job CAN NOT be run");
}
}
public void cancelBuild(Cause cause, ParametersAction parametersAction) {
String buildId = getParameterValueIfExist(parametersAction, UftConstants.BUILD_ID_PARAMETER_NAME);
String suiteId = getParameterValueIfExist(parametersAction, SdkConstants.JobParameters.SUITE_ID_PARAMETER_NAME);
String suiteRunId = getParameterValueIfExist(parametersAction, SdkConstants.JobParameters.SUITE_RUN_ID_PARAMETER_NAME);
String releaseExecutionId = getParameterValueIfExist(parametersAction, SdkConstants.JobParameters.OCTANE_AUTO_ACTION_EXECUTION_ID_PARAMETER_NAME);
if (buildId != null) {
logger.info(String.format("cancelBuild for %s, buildId=%s", job.getFullName(), buildId));
Run aBuild = (job).getBuild(buildId);
logger.info(String.format("cancelBuild for %s, buildId=%s - is done", job.getFullName(), buildId));
if (aBuild == null) {
logger.warn(String.format("Cannot stop : build %s is not found", buildId));
return;
}
stopBuild(aBuild);
} else {
FoundInfo foundInfo = new FoundInfo();
String paramToSearch;
String paramValueToSearch;
if (SdkStringUtils.isNotEmpty(releaseExecutionId)) {
paramToSearch = SdkConstants.JobParameters.OCTANE_AUTO_ACTION_EXECUTION_ID_PARAMETER_NAME;
paramValueToSearch = releaseExecutionId;
} else if (SdkStringUtils.isNotEmpty(suiteRunId)) {
paramToSearch = SdkConstants.JobParameters.SUITE_RUN_ID_PARAMETER_NAME;
paramValueToSearch = suiteRunId;
} else {
throw new IllegalArgumentException("Cannot cancel job as no identification parameters was passed");
}
logger.info(String.format("cancelBuild for %s, %s=%s", job.getFullName(), paramToSearch, paramValueToSearch));
Queue queue = Jenkins.get().getQueue();
Queue.Task queueTaskJob = (Queue.Task) job;
queue.getItems(queueTaskJob).forEach(item -> {
item.getActions(ParametersAction.class).forEach(action -> {
if (!foundInfo.found && checkIfParamExistAndEqual(action, paramToSearch, paramValueToSearch)) {
try {
logger.info("canceling item in queue : " + item);
queue.cancel(item);
logger.info("Item in queue is cancelled item : " + item);
foundInfo.found = true;
} catch (Exception e) {
logger.warn("Failed to cancel '" + item + "' in queue : " + e.getMessage(), e);
}
}
});
});
job.getBuilds().forEach(build -> {
if (!foundInfo.found) {
Run run = (Run)build;
run.getActions(ParametersAction.class).forEach(action -> {
if (checkIfParamExistAndEqual(action, paramToSearch, paramValueToSearch)) {
stopBuild(run);
foundInfo.found = true;
}
});
}
});
}
}
public CIBuildStatusInfo getBuildStatus(String paramName, String paramValue) {
CIBuildStatusInfo status = DTOFactory.getInstance().newDTO(CIBuildStatusInfo.class)
.setBuildStatus(CIBuildStatus.UNAVAILABLE)
.setJobCiId(this.getTranslatedJobName())
.setParamName(paramName)
.setParamValue(paramValue);
String buildId = UftConstants.BUILD_ID_PARAMETER_NAME.equals(paramName) ? paramValue : null;
if (buildId != null) {
try {
int buildNum = Integer.parseInt(buildId);
Run aBuild = job.getBuildByNumber(buildNum);
if (aBuild == null) {
status.setBuildStatus(CIBuildStatus.UNAVAILABLE);
} else {
status.setBuildCiId(BuildHandlerUtils.getBuildCiId(aBuild));
status.setAllBuildParams(ParameterProcessors.getInstances(aBuild));
if (aBuild.isBuilding()) {
status.setBuildStatus(CIBuildStatus.RUNNING);
} else {
status.setBuildStatus(CIBuildStatus.FINISHED);
status.setResult(BuildHandlerUtils.translateRunResult(aBuild));
}
}
} catch (NumberFormatException e) {
throw new RuntimeException("Failed to parse build id " + buildId);
}
} else {
FoundInfo foundInfo = new FoundInfo();
if (job instanceof Queue.Task) {
Queue.Task queueTaskJob = (Queue.Task) job;
Queue queue = Jenkins.get().getQueue();
queue.getItems(queueTaskJob).forEach(item -> {
item.getActions(ParametersAction.class).forEach(action -> {
if (!foundInfo.found && checkIfParamExistAndEqual(action, paramName, paramValue)) {
status.setBuildStatus(CIBuildStatus.QUEUED);
foundInfo.found = true;
}
});
});
}
job.getBuilds().forEach(build -> {
if (!foundInfo.found) {
Run aBuild = (Run) build;
aBuild.getActions(ParametersAction.class).forEach(action -> {
if (checkIfParamExistAndEqual(action, paramName, paramValue)) {
if (aBuild.isBuilding()) {
status.setBuildStatus(CIBuildStatus.RUNNING);
} else {
status.setBuildStatus(CIBuildStatus.FINISHED);
status.setResult(BuildHandlerUtils.translateRunResult(aBuild));
status.setEnvironmentOutputtedParameters(OutputEnvironmentParametersHelper.getOutputEnvironmentParams(aBuild));
}
status.setAllBuildParams(ParameterProcessors.getInstances(aBuild));
status.setBuildCiId(BuildHandlerUtils.getBuildCiId(aBuild));
foundInfo.found = true;
}
});
}
});
}
return status;
}
private String getParameterValueIfExist(ParametersAction parametersAction, String paramName) {
ParameterValue pv = parametersAction.getParameter(paramName);
if (pv != null) {
return (String) pv.getValue();
} else {
return null;
}
}
protected void stopBuild(Run run) {
AbstractBuild aBuild = (AbstractBuild)run;
try {
aBuild.doStop();
logger.info("Build is stopped : " + aBuild.getProject().getDisplayName() + aBuild.getDisplayName());
} catch (Exception e) {
logger.warn("Failed to stop build '" + aBuild.getDisplayName() + "' :" + e.getMessage(), e);
}
}
private boolean checkIfParamExistAndEqual(ParametersAction parametersAction, String paramName, String expectedValue) {
ParameterValue pv = parametersAction.getParameter(paramName);
return (SdkStringUtils.isNotEmpty(expectedValue) && pv != null && pv.getValue().equals(expectedValue));
}
/**
* Retrieve Job's CI ID
* return the job name, in case of a folder job, this method returns the refactored
* name that matches the required pattern.
*
* @return Job's CI ID
*/
public String getTranslatedJobName() {
if (JobProcessorFactory.FOLDER_JOB_NAME.equals(job.getParent().getClass().getName())) {
String jobPlainName = job.getFullName(); // e.g: myFolder/myJob
return BuildHandlerUtils.translateFolderJobName(jobPlainName);
} else {
return job.getName();
}
}
public void buildStructure(Set<Job> processedJobs) {
processedJobs.add(job);
buildStructureInternal(processedJobs);
processedJobs.remove(job);
isProcessed = true;
}
protected void buildStructureInternal(Set<Job> processedJobs){
}
/**
* Discover an internal phases of the Job
*
* @return list of phases
*/
public List<PipelinePhase> getInternals() {
if (!isProcessed) {
buildStructure(new HashSet<>());
}
return internals;
}
/**
* Discover a post build phases of the Job
*
* @return list of phases
*/
public List<PipelinePhase> getPostBuilds() {
if (!isProcessed) {
buildStructure(new HashSet<>());
}
return postBuilds;
}
/**
* Internal API
* Processes and prepares Job's children for future use - internal flow
*
* @param builders Job's builders
* @param job Job to process
* @param processedJobs previously processed Jobs in this Job's hierarchical chain in order to break the recursive flows
*/
void processBuilders(List<Builder> builders, Job job, Set<Job> processedJobs) {
this.processBuilders(builders, job, "", processedJobs);
}
/**
* Internal API
* Processes and prepares Job's children for future use - internal flow
*
* @param builders Job's builders
* @param job Job to process
* @param phasesName Targeted phase name in case of available one
* @param processedJobs previously processed Jobs in this Job's hierarchical chain in order to break the recursive flows
*/
void processBuilders(List<Builder> builders, Job job, String phasesName, Set<Job> processedJobs) {
for (Builder builder : builders) {
AbstractBuilderProcessor.processInternalBuilders(builder, job, phasesName, internals, processedJobs);
}
}
/**
* Internal API
* Processes and prepares Job's children for future use - post build flow
*
* @param job Job to process
* @param processedJobs previously processed Jobs in this Job's hierarchical chain in order to break the recursive flows
*/
@SuppressWarnings("unchecked")
void processPublishers(Job job, Set<Job> processedJobs) {
if (job instanceof AbstractProject) {
AbstractProject project = (AbstractProject) job;
processedJobs.add(job);
AbstractBuilderProcessor builderProcessor;
List<Publisher> publishers = project.getPublishersList();
for (Publisher publisher : publishers) {
builderProcessor = null;
if (publisher.getClass().getName().equals(JobProcessorFactory.SIMPLE_BUILD_TRIGGER)) {
builderProcessor = new BuildTriggerProcessor(publisher, project, processedJobs);
} else if (publisher.getClass().getName().equals(JobProcessorFactory.PARAMETRIZED_BUILD_TRIGGER)) {
builderProcessor = new ParameterizedTriggerProcessor(publisher, project, "", processedJobs);
}
if (builderProcessor != null) {
postBuilds.addAll(builderProcessor.getPhases());
} else {
logger.debug("not yet supported publisher (post build) action: " + publisher.getClass().getName());
}
}
processedJobs.remove(job);
}
}
private static class FoundInfo{
public boolean found;
}
}