jenkinsci/hpe-application-automation-tools-plugin

View on GitHub
src/main/java/com/microfocus/application/automation/tools/run/SseBuilder.java

Summary

Maintainability
C
7 hrs
Test Coverage
/*
 * 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-2023 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.run;

import java.io.IOException;
import java.io.PrintStream;
import java.io.StringWriter;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

import com.microfocus.application.automation.tools.JenkinsUtils;
import com.microfocus.application.automation.tools.model.AlmServerSettingsModel;
import com.microfocus.application.automation.tools.model.CdaDetails;
import com.microfocus.application.automation.tools.model.EnumDescription;
import com.microfocus.application.automation.tools.model.SseModel;
import com.microfocus.application.automation.tools.settings.AlmServerSettingsGlobalConfiguration;
import com.microfocus.application.automation.tools.sse.result.model.junit.Testcase;
import com.microfocus.application.automation.tools.sse.result.model.junit.Testsuite;
import com.microfocus.application.automation.tools.sse.result.model.junit.Testsuites;
import com.microfocus.application.automation.tools.sse.sdk.Logger;
import org.apache.commons.lang.StringUtils;
import org.jenkinsci.Symbol;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.QueryParameter;

import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardUsernameListBoxModel;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.domains.URIRequirementBuilder;
import com.cloudbees.plugins.credentials.matchers.IdMatcher;
import com.microfocus.application.automation.tools.sse.SSEBuilderPerformer;

import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.Util;
import hudson.model.*;
import hudson.model.queue.Tasks;
import hudson.security.ACL;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.Builder;
import hudson.util.FormValidation;
import hudson.util.ListBoxModel;
import hudson.util.VariableResolver;
import jenkins.tasks.SimpleBuildStep;

import static com.microfocus.application.automation.tools.Messages.CompanyName;
import static com.microfocus.application.automation.tools.Messages.SseBuilderStepName;

/***
 * This Jenkins plugin contains an unofficial implementation of some of the elements of the HPE ALM
 * Lab Management SDK. Users are free to use this plugin as they wish, but HPE does not take
 * responsibility for supporting or providing backwards compatibility for the functionality herein.
 *
 *
 * @author Effi Bar-She'an
 * @author Dani Schreiber
 *
 */
public class SseBuilder extends Builder implements SimpleBuildStep {

    private SseModel _sseModel;
    private String _fileName;

    private String almServerName;
    private String credentialsId;
    private String clientType;
    private String almDomain;
    private String almProject;
    private String description;
    private String runType;
    private String almEntityId;
    private String timeslotDuration;
    private String postRunAction;
    private String environmentConfigurationId;
    private CdaDetails cdaDetails;

    //Databound setters and getters.
    public String getAlmServerName() { return almServerName; }
    public String getCredentialsId() { return credentialsId; }
    public String getClientType() { return clientType; }
    public String getAlmDomain() { return almDomain; }
    public String getAlmProject() { return almProject; }
    public String getDescription() { return description; }
    public String getRunType() { return runType; }
    public String getAlmEntityId() { return almEntityId; }
    public String getTimeslotDuration() { return timeslotDuration; }
    public String getPostRunAction() { return postRunAction; }
    public String getEnvironmentConfigurationId() { return environmentConfigurationId; }
    public CdaDetails getCdaDetails() { return cdaDetails; }

    public boolean isCdaDetailsChecked() {
        return cdaDetails != null;
    }

    @DataBoundSetter
    public void setDescription(String description) { this.description = description; }

    @DataBoundSetter
    public void setPostRunAction(String postRunAction) { this.postRunAction = postRunAction; }

    @DataBoundSetter
    public void setEnvironmentConfigurationId(String environmentConfigurationId) {
        this.environmentConfigurationId = environmentConfigurationId;
    }

    @DataBoundSetter
    public void setCdaDetails(CdaDetails cdaDetails) { this.cdaDetails = cdaDetails; }

    /**
     * Should only contains mandatory properties.
     */
    @DataBoundConstructor
    public SseBuilder(String almServerName,
            String almProject,
            String credentialsId,
            String clientType,
            String almDomain,
            String runType,
            String almEntityId,
            String timeslotDuration) {

        this.almServerName = almServerName;
        this.credentialsId = credentialsId;
        this.almProject = almProject;
        this.almDomain = almDomain;
        this.timeslotDuration = timeslotDuration;
        this.runType = runType;
        this.almEntityId = almEntityId;
        this.clientType = clientType;
    }

    @Override
    public void perform(Run<?, ?> build, FilePath workspace, Launcher launcher, TaskListener listener)
            throws InterruptedException, IOException {

        PrintStream logger = listener.getLogger();

        UsernamePasswordCredentials credentials = getCredentialsById(credentialsId, build, logger);

        _sseModel = new SseModel(
                almServerName,
                credentials.getUsername(),
                credentials.getPassword().getPlainText(),
                almDomain,
                clientType,
                almProject,
                runType,
                almEntityId,
                timeslotDuration,
                description,
                postRunAction,
                environmentConfigurationId,
                cdaDetails);

        _sseModel.setAlmServerUrl(getServerUrl(_sseModel.getAlmServerName()));

        VariableResolver<String> varResolver = new VariableResolver.ByMap<String>(build.getEnvironment(listener));
        Testsuites testsuites = execute(build, logger, varResolver);

        String resultsFilename = generateResultsFilename();
        FilePath resultsFilePath = workspace.child(resultsFilename);
        Result resultStatus = createRunResults(resultsFilePath, testsuites, logger);
        provideStepResultStatus(resultStatus, build, logger);

        //params used when run with Pipeline
        ParametersAction parameterAction = build.getAction(ParametersAction.class);
        List<ParameterValue> newParams = (parameterAction != null) ? new ArrayList<>(parameterAction.getAllParameters()) : new ArrayList<>();
        newParams.add(new StringParameterValue("buildStepName", "RunFromAlmLabManagementBuilder"));
        newParams.add(new StringParameterValue("resultsFilename", resultsFilename));
        build.addOrReplaceAction(new ParametersAction(newParams));
    }

    /**
     * Get user name password credentials by id.
     */
    private UsernamePasswordCredentials getCredentialsById(String credentialsId, Run<?, ?> run, PrintStream logger) {
        if (StringUtils.isBlank(credentialsId)) {
            throw new NullPointerException("credentials is not configured.");
        }

        UsernamePasswordCredentials credentials = CredentialsProvider.findCredentialById(credentialsId,
                StandardUsernamePasswordCredentials.class,
                run,
                URIRequirementBuilder.create().build());

        if (credentials == null) {
            logger.println("Can not find credentials with the credentialsId:" + credentialsId);
        }
        return credentials;
    }

    public AlmServerSettingsModel getAlmServerSettingsModel() {
        AlmServerSettingsModel ret = null;
        for (AlmServerSettingsModel almServer : getDescriptor().getAlmServers()) {
            if (this.almServerName.equals(almServer.getAlmServerName())) {
                ret = almServer;
                break;
            }
        }
        return ret;
    }

    private void provideStepResultStatus(
            Result resultStatus,
            Run<?, ?> build,
            PrintStream logger) {

        logger.println(String.format("Result Status: %s", resultStatus.toString()));
        build.setResult(resultStatus);

    }

    private Testsuites execute(
            Run<?, ?> build,
            PrintStream logger,
            VariableResolver<String> buildVariableResolver) throws InterruptedException {

        Testsuites ret = null;
        SSEBuilderPerformer performer = null;
        try {
            performer = new SSEBuilderPerformer();
            ret = execute(performer, logger, buildVariableResolver);
        } catch (InterruptedException e) {
            build.setResult(Result.ABORTED);
            stop(performer, logger);
            throw e;
        } catch (Exception cause) {
            build.setResult(Result.FAILURE);
            logger.print(String.format("Failed to execute test, Exception: %s", cause.getMessage()));
        }

        return ret;
    }

    private Result createRunResults(FilePath filePath, Testsuites testsuites, PrintStream logger) {
        Result ret = Result.SUCCESS;
        try {
            if (testsuites != null && !testsuites.getTestsuite().isEmpty()) {
                StringWriter writer = new StringWriter();
                JAXBContext context;
                Thread t = Thread.currentThread();
                ClassLoader orig = t.getContextClassLoader();
                t.setContextClassLoader(SseBuilder.class.getClassLoader());
                try {
                    context = JAXBContext.newInstance(Testsuites.class);
                } finally {
                    t.setContextClassLoader(orig);
                }
                Marshaller marshaller = context.createMarshaller();
                marshaller.marshal(testsuites, writer);
                filePath.write(writer.toString(), null);
                if (containsErrors(testsuites.getTestsuite())) {
                    ret = Result.UNSTABLE;
                }
            } else {
                logger.println("Empty Results");
                ret = Result.FAILURE;
            }
        } catch (Exception cause) {
            logger.print(String.format(
                    "Failed to create run results, Exception: %s",
                    cause.getMessage()));
            ret = Result.FAILURE;
        }

        return ret;
    }

    private boolean containsErrors(List<Testsuite> testsuites) {

        boolean ret = false;
        for (Testsuite testsuite : testsuites) {
            for (Testcase testcase : testsuite.getTestcase()) {
                if ("error".equals(testcase.getStatus())) {
                    ret = true;
                    break;
                }
            }
        }

        return ret;
    }

    private String generateResultsFilename() {

        Format formatter = new SimpleDateFormat("ddMMyyyyHHmmssSSS");
        String time = formatter.format(new Date());
        _fileName = String.format("Results%s.xml", time);
        return _fileName;
    }

    private void stop(SSEBuilderPerformer performer, PrintStream logger) {

        try {
            if (performer != null) {
                performer.stop();
            }
        } catch (Exception cause) {
            logger.println(String.format("Failed to stop BVS. Exception: %s", cause.getMessage()));
        }
    }

    private Testsuites execute(
            SSEBuilderPerformer performer,
            final PrintStream logger,
            VariableResolver<String> buildVariableResolver) throws InterruptedException,
            IOException {

        return performer.start(_sseModel, new Logger() {

            @Override
            public void log(String message) {

                logger.println(message);
            }

            @Override
            public void error(String message) {
                log(message);
            }
        }, buildVariableResolver);
    }

    public String getServerUrl(String almServerName) {

        String ret = "";
        AlmServerSettingsModel[] almServers = getDescriptor().getAlmServers();
        if (almServers != null && almServers.length > 0) {
            for (AlmServerSettingsModel almServer : almServers) {
                if (almServerName.equals(almServer.getAlmServerName())) {
                    ret = almServer.getAlmServerUrl();
                    break;
                }
            }
        }

        return ret;
    }

    public SseModel getSseModel() {

        return _sseModel;
    }

    public String getRunResultsFileName() {

        return _fileName;
    }

    @Override
    public DescriptorImpl getDescriptor() {

        return (DescriptorImpl) super.getDescriptor();
    }

    // This indicates to Jenkins that this is an implementation of an extension point
    @Extension
    // To expose this builder in the Snippet Generator.
    @Symbol("sseBuild")
    public static final class DescriptorImpl extends BuildStepDescriptor<Builder> {

        public DescriptorImpl() {
            load();
        }

        @Override
        public boolean isApplicable(
                @SuppressWarnings("rawtypes") Class<? extends AbstractProject> jobType) {

            return true;
        }

        @Override
        public String getDisplayName() {
            return SseBuilderStepName(CompanyName());
        }

        public boolean hasAlmServers() {
            return AlmServerSettingsGlobalConfiguration.getInstance().hasAlmServers();
        }

        public AlmServerSettingsModel[] getAlmServers() {
            return AlmServerSettingsGlobalConfiguration.getInstance().getInstallations();
        }

        public FormValidation doCheckTimeslotDuration(@QueryParameter String value) {
            if (StringUtils.isBlank(value)) {
                return FormValidation.error("Timeslot duration must be set");
            }

            String val1 = value.trim();

            if (!StringUtils.isNumeric(val1)) {
                return FormValidation.error("Timeslot duration must be a number");
            }

            if (Integer.valueOf(val1) < 30) {
                return FormValidation.error("Timeslot duration must be higher than 30");
            }

            return FormValidation.ok();
        }

        public FormValidation doCheckAlmDomain(@QueryParameter String value) {

            FormValidation ret = FormValidation.ok();
            if (StringUtils.isBlank(value)) {
                ret = FormValidation.error("Domain must be set");
            }

            return ret;
        }

        public FormValidation doCheckAlmProject(@QueryParameter String value) {

            FormValidation ret = FormValidation.ok();
            if (StringUtils.isBlank(value)) {
                ret = FormValidation.error("Project must be set");
            }

            return ret;
        }

        public FormValidation doCheckAlmEntityId(@QueryParameter String value) {

            FormValidation ret = FormValidation.ok();
            if (StringUtils.isBlank(value)) {
                ret = FormValidation.error("Entity must be set.");
            }

            return ret;
        }

        public List<EnumDescription> getRunTypes() {

            return SseModel.getRunTypes();
        }

        public List<EnumDescription> getPostRunActions() {

            return SseModel.getPostRunActions();
        }

        public List<EnumDescription> getDeploymentActions() {

            return CdaDetails.getDeploymentActions();
        }

        public static List<EnumDescription> getDeprovisioningActions() {

            return CdaDetails.getDeprovisioningActions();
        }

        /**
         * To fill in the credentials drop down list which's field is 'credentialsId'.
         * This method's name works with tag <c:select/>.
         */
        public ListBoxModel doFillCredentialsIdItems(@AncestorInPath Item project,
                @QueryParameter String credentialsId) {

            if (project == null || !project.hasPermission(Item.CONFIGURE)) {
                return new StandardUsernameListBoxModel().includeCurrentValue(credentialsId);
            }
            return new StandardUsernameListBoxModel()
                    .includeEmptyValue()
                    .includeAs(
                            project instanceof Queue.Task ? Tasks.getAuthenticationOf((Queue.Task) project) : ACL.SYSTEM,
                            project,
                            StandardUsernamePasswordCredentials.class,
                            URIRequirementBuilder.create().build())
                    .includeCurrentValue(credentialsId);
        }

        public FormValidation doCheckCredentialsId(@AncestorInPath Item project,
                @QueryParameter String url,
                @QueryParameter String value) {
            if (project == null || !project.hasPermission(Item.EXTENDED_READ)) {
                return FormValidation.ok();
            }

            value = Util.fixEmptyAndTrim(value);
            if (value == null) {
                return FormValidation.ok();
            }

            url = Util.fixEmptyAndTrim(url);
            if (url == null)
            // not set, can't check
            {
                return FormValidation.ok();
            }

            if (url.indexOf('$') >= 0)
            // set by variable, can't check
            {
                return FormValidation.ok();
            }

            for (ListBoxModel.Option o : CredentialsProvider.listCredentials(
                        StandardUsernamePasswordCredentials.class,
                        project,
                        project instanceof Queue.Task ? Tasks.getAuthenticationOf((Queue.Task) project) : ACL.SYSTEM,
                        URIRequirementBuilder.create().build(),
                        new IdMatcher(value))) {

                if (StringUtils.equals(value, o.value)) {
                    return FormValidation.ok();
                }
            }
            // no credentials available, can't check
            return FormValidation.warning("Cannot find any credentials with id " + value);
        }

        public boolean getHasConfigurePermission() {
            return JenkinsUtils.hasItemConfigurePermission();
        }
    }
}