io.fabric8.maven.rt.BaseBoosterIT.java Source code

Java tutorial

Introduction

Here is the source code for io.fabric8.maven.rt.BaseBoosterIT.java

Source

/*
 * Copyright 2016 Red Hat, Inc.
 *
 * Red Hat licenses this file to you 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 io.fabric8.maven.rt;

import io.fabric8.kubernetes.api.KubernetesHelper;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.PodList;
import io.fabric8.kubernetes.client.ConfigBuilder;
import io.fabric8.openshift.api.model.DeploymentConfig;
import io.fabric8.openshift.api.model.DeploymentConfigList;
import io.fabric8.openshift.api.model.Route;
import io.fabric8.openshift.api.model.RouteList;
import io.fabric8.openshift.client.DefaultOpenShiftClient;
import io.fabric8.openshift.client.OpenShiftClient;
import okhttp3.*;
import org.apache.commons.io.FileUtils;
import org.apache.maven.model.*;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
import org.arquillian.smart.testing.rules.git.GitCloner;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Repository;
import org.jboss.shrinkwrap.resolver.api.maven.embedded.EmbeddedMaven;
import org.json.JSONObject;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

public class BaseBoosterIT {

    protected final String fabric8PluginGroupId = "io.fabric8";

    protected final String fabric8MavenPluginKey = "io.fabric8:fabric8-maven-plugin";

    protected final String fabric8PluginArtifactId = "fabric8-maven-plugin";

    protected String testsuiteNamespace;

    protected String testsuiteRepositoryArtifactId;

    protected final String fmpConfigurationFile = "/fmp-plugin-config.xml";

    protected OpenShiftClient openShiftClient;

    protected final int APPLICATION_POD_WAIT_POLLS = 100;

    protected final static Logger logger = Logger.getLogger(BaseBoosterIT.class.getSimpleName());

    protected GitCloner gitCloner;

    protected enum HttpRequestType {
        GET, POST, PUT, DELETE;
    };

    private Repository cloneRepositoryUsingHttp(String repositoryUrl, String releasedVersionTag)
            throws IOException, GitAPIException {
        gitCloner = new GitCloner(repositoryUrl);
        Repository repository = gitCloner.cloneRepositoryToTempFolder();
        Git.wrap(repository).checkout().setCreateBranch(true).setName("test-branch")
                .setStartPoint("tags/" + releasedVersionTag).call();
        return repository;
    }

    private void modifyPomFileToProjectVersion(Repository aRepository, String relativePomPath)
            throws IOException, XmlPullParserException {
        /**
         * Read Maven model from the project pom file(Here the pom file is not the test repository is cloned
         * for the test suite. It refers to the rt/ project and fetches the current version of fabric8-maven-plugin
         * (any SNAPSHOT version) and updates that accordingly in the sample cloned project's pom.
         */
        File clonedRepositoryPomFile = new File(aRepository.getWorkTree().getAbsolutePath(), relativePomPath);
        String fmpCurrentVersion = readPomModelFromFile(new File("pom.xml")).getVersion();
        Model model = readPomModelFromFile(clonedRepositoryPomFile);
        testsuiteRepositoryArtifactId = model.getArtifactId();

        // Check if fmp is not present in openshift profile
        model = updatePomIfFmpNotPresent(model, clonedRepositoryPomFile);
        Build build = model.getBuild();

        /*
         * Handle the scenarios where build is in outermost scope or present
         * specifically in openshift profile.
         */
        List<Profile> profiles = model.getProfiles();
        if (build != null
                && build.getPluginsAsMap().get(fabric8PluginGroupId + ":" + fabric8PluginArtifactId) != null) {
            build.getPluginsAsMap().get(fabric8PluginGroupId + ":" + fabric8PluginArtifactId)
                    .setVersion(fmpCurrentVersion);
        } else {
            for (Profile profile : profiles) {
                if (profile.getBuild() != null && profile.getBuild().getPluginsAsMap()
                        .get(fabric8PluginGroupId + ":" + fabric8PluginArtifactId) != null) {
                    profile.getBuild().getPluginsAsMap().get(fabric8PluginGroupId + ":" + fabric8PluginArtifactId)
                            .setVersion(fmpCurrentVersion);
                }
            }
        }

        // Write back the updated model to the pom file
        writePomModelToFile(clonedRepositoryPomFile, model);
    }

    private Model updatePomIfFmpNotPresent(Model projectModel, File pomFile)
            throws XmlPullParserException, IOException {
        if (getProfileIndexUsingFmp(projectModel, fabric8MavenPluginKey) < 0)
            projectModel = writeOpenShiftProfileInPom(projectModel, pomFile);

        return projectModel;
    }

    private Model writeOpenShiftProfileInPom(Model projectModel, File pomFile)
            throws XmlPullParserException, IOException {
        final List<PluginExecution> executions = new ArrayList<>();

        PluginExecution aPluginExecution = new PluginExecution();
        aPluginExecution.setId("fmp");
        aPluginExecution.addGoal("resource");
        aPluginExecution.addGoal("build");

        executions.add(aPluginExecution);

        final Plugin fabric8Plugin = new Plugin();
        fabric8Plugin.setGroupId(fabric8PluginGroupId);
        fabric8Plugin.setArtifactId(fabric8PluginArtifactId);
        fabric8Plugin.setExecutions(executions);

        Build build = new Build();
        build.getPlugins().add(fabric8Plugin);

        int nOpenShiftIndex;
        Profile fmpProfile;
        if ((nOpenShiftIndex = getProfileIndexWithName(projectModel, "openshift")) > 0) { // update existing profile
            fmpProfile = projectModel.getProfiles().get(nOpenShiftIndex);
            fmpProfile.setBuild(build);
            projectModel.getProfiles().set(nOpenShiftIndex, fmpProfile);
        } else { // if not present, simply create a profile names openshift which would contain fmp.
            fmpProfile = new Profile();
            fmpProfile.setId("openshift");
            fmpProfile.setBuild(build);
            projectModel.addProfile(fmpProfile);
        }
        writePomModelToFile(pomFile, projectModel);

        return projectModel;
    }

    protected static Model readPomModelFromFile(File aFileObj) throws XmlPullParserException, IOException {
        MavenXpp3Reader reader = new MavenXpp3Reader();
        return reader.read(new FileInputStream(aFileObj));
    }

    protected void writePomModelToFile(File aFileObj, Model model) throws IOException {
        MavenXpp3Writer writer = new MavenXpp3Writer();
        writer.write(new FileOutputStream(aFileObj), model);
    }

    protected void updateSourceCode(Repository repository, String relativePomPath)
            throws XmlPullParserException, IOException {
        MavenXpp3Reader reader = new MavenXpp3Reader();
        String baseDir = repository.getWorkTree().getAbsolutePath();
        Model model = reader.read(new FileInputStream(new File(baseDir, relativePomPath)));

        Dependency dependency = new Dependency();
        dependency.setGroupId("org.apache.commons");
        dependency.setArtifactId("commons-lang3");
        dependency.setVersion("3.5");
        model.getDependencies().add(dependency);

        MavenXpp3Writer writer = new MavenXpp3Writer();
        writer.write(new FileOutputStream(new File(baseDir, relativePomPath)), model);
        model.getArtifactId();
    }

    protected Repository setupSampleTestRepository(String repositoryUrl, String relativePomPath,
            String releasedVersionTag) throws IOException, GitAPIException, XmlPullParserException {
        openShiftClient = new DefaultOpenShiftClient(new ConfigBuilder().build());
        testsuiteNamespace = openShiftClient.getNamespace();
        Repository repository = cloneRepositoryUsingHttp(repositoryUrl, releasedVersionTag);

        modifyPomFileToProjectVersion(repository, relativePomPath);
        return repository;
    }

    protected String getCurrentFMPVersion() throws Exception {
        return readPomModelFromFile(new File("pom.xml")).getVersion();
    }

    protected void runEmbeddedMavenBuild(Repository sampleRepository, String goals, String profiles) {
        String baseDir = sampleRepository.getWorkTree().getAbsolutePath();
        EmbeddedMaven.forProject(baseDir + "/pom.xml").setGoals(goals).setQuiet(true).setProfiles(profiles).build();
    }

    protected void cleanSampleTestRepository() {
        this.gitCloner.removeClone();
        this.openShiftClient.close();
    }

    protected Route getApplicationRouteWithName(String name) {
        RouteList aRouteList = openShiftClient.routes().inNamespace(testsuiteNamespace).list();
        for (Route aRoute : aRouteList.getItems()) {
            if (aRoute.getMetadata().getName().equals(name)) {
                return aRoute;
            }
        }
        return null;
    }

    /**
     * Just makes a basic GET request to the url provided as parameter and returns Response.
     *
     * @param hostUrl
     * @return
     * @throws Exception
     */
    protected Response makeHttpRequest(HttpRequestType requestType, String hostUrl, String params)
            throws IOException {
        OkHttpClient okHttpClient = new OkHttpClient();
        MediaType json = MediaType.parse("application/json; charset=utf-8");
        params = (params == null ? new JSONObject().toString() : params);
        Request request = null;
        RequestBody requestBody = RequestBody.create(json, params);

        switch (requestType) {
        case GET:
            request = new Request.Builder().url(hostUrl).get().build();
            break;
        case POST:
            request = new Request.Builder().url(hostUrl).post(requestBody).build();
            break;
        case PUT:
            request = new Request.Builder().url(hostUrl).put(requestBody).build();
            break;
        case DELETE:
            request = new Request.Builder().url(hostUrl).delete(requestBody).build();
            break;
        default:
            logger.info("No valid Http request type specified, using GET instread.");
            request = new Request.Builder().url(hostUrl).get().build();
        }
        Response response = okHttpClient.newCall(request).execute();
        if (logger.isLoggable(Level.INFO)) {
            logger.info(
                    String.format("[%s] %s %s", requestType.name(), hostUrl, HttpStatus.getCode(response.code())));
        }

        return response;
    }

    protected int exec(String command) throws IOException, InterruptedException {
        Process child = Runtime.getRuntime().exec(command);
        child.waitFor();

        BufferedReader reader = new BufferedReader(new InputStreamReader(child.getInputStream()));
        String line = null;
        while ((line = reader.readLine()) != null) {
            logger.info(line);
        }

        if (child.exitValue() != 0)
            logger.log(Level.WARNING,
                    String.format("Exec for : %s returned status : %d", command, child.exitValue()));
        return child.exitValue();
    }

    /**
     * Appends some annotation properties to the fmp's configuration in test repository's pom
     * just to distinguish whether the application is re-deployed or not.
     *
     * @param testRepository
     * @throws Exception
     */
    protected void addRedeploymentAnnotations(Repository testRepository, String relativePomPath,
            String annotationKey, String annotationValue, String fmpConfigFragmentFile)
            throws IOException, XmlPullParserException {
        File pomFile = new File(testRepository.getWorkTree().getAbsolutePath(), relativePomPath);
        Model model = readPomModelFromFile(pomFile);

        File pomFragment = new File(getClass().getResource(fmpConfigFragmentFile).getFile());
        String pomFragmentStr = String.format(FileUtils.readFileToString(pomFragment), annotationKey,
                annotationValue, annotationKey, annotationValue);

        Xpp3Dom configurationDom = Xpp3DomBuilder.build(new ByteArrayInputStream(pomFragmentStr.getBytes()),
                "UTF-8");

        int nOpenShiftProfile = getProfileIndexUsingFmp(model, fabric8MavenPluginKey);
        model.getProfiles().get(nOpenShiftProfile).getBuild().getPluginsAsMap().get(fabric8MavenPluginKey)
                .setConfiguration(configurationDom);
        writePomModelToFile(pomFile, model);
    }

    protected int getProfileIndexUsingFmp(Model aPomModel, String pluginName) {
        List<Profile> profiles = aPomModel.getProfiles();
        for (int nIndex = 0; nIndex < profiles.size(); nIndex++) {
            if (profiles.get(nIndex).getBuild() != null
                    && profiles.get(nIndex).getBuild().getPluginsAsMap().containsKey(pluginName))
                return nIndex;
        }
        logger.log(Level.WARNING, "No profile found in project's pom.xml using fmp");
        return -1;
    }

    protected int getProfileIndexWithName(Model aPomModel, String profileId) {
        List<Profile> profiles = aPomModel.getProfiles();
        for (int nIndex = 0; nIndex < profiles.size(); nIndex++) {
            if (profiles.get(nIndex).getId().equals(profileId))
                return nIndex;
        }
        logger.log(Level.WARNING, String.format("No profile found in project's pom.xml containing %s", profileId));
        return -1;
    }

    /**
     * A method to check Re-deployment scenario. We append some annotations in all the resources and
     * check that we have those after deployment for 2nd time. This is basically to distinguish
     * deployment's versions.
     *
     * @param key
     * @return
     */
    protected boolean checkDeploymentsForAnnotation(String key) {
        DeploymentConfigList deploymentConfigs = openShiftClient.deploymentConfigs().inNamespace(testsuiteNamespace)
                .list();
        for (DeploymentConfig aDeploymentConfig : deploymentConfigs.getItems()) {
            if (aDeploymentConfig.getMetadata() != null && aDeploymentConfig.getMetadata().getAnnotations() != null
                    && aDeploymentConfig.getMetadata().getAnnotations().containsKey(key))
                return true;
        }
        return false;
    }

    /**
     * It watches over application pod until it becomes ready to serve.
     *
     * @throws Exception
     */
    protected void waitTillApplicationPodStarts() throws InterruptedException {
        logger.info("Waiting to application pod .... ");

        int nPolls = 0;
        // Keep polling till 5 minutes
        while (nPolls < APPLICATION_POD_WAIT_POLLS) {
            PodList podList = openShiftClient.pods().withLabel("app", testsuiteRepositoryArtifactId).list();
            for (Pod pod : podList.getItems()) {
                logger.info("waitTillApplicationPodStarts() -> Pod : " + pod.getMetadata().getName()
                        + ", isReady : " + KubernetesHelper.isPodReady(pod));
                if (KubernetesHelper.isPodReady(pod)) {
                    logger.info("OK  ... Pod wait over.");
                    TimeUnit.SECONDS.sleep(10);
                    return;
                }
            }
            TimeUnit.SECONDS.sleep(5);
            nPolls++;
        }
        throw new AssertionError(
                "Pod wait timeout! Could not find application pod for " + testsuiteRepositoryArtifactId);
    }

    /**
     * This variation is used in order to check for the redeployment scenario, since some
     * annotations are added while making changes in source code, and those are checked so
     * that we are able to differentiate between the redeployed pod and the previously
     * existing pod instance from previous deployment.
     *
     * @param key
     * @param value
     * @throws Exception
     */
    protected void waitTillApplicationPodStarts(String key, String value) throws InterruptedException {
        logger.info("Waiting for application pod .... ");

        int nPolls = 0;
        // Keep polling till 5 minutes
        while (nPolls < APPLICATION_POD_WAIT_POLLS) {
            PodList podList = openShiftClient.pods().withLabel("app", testsuiteRepositoryArtifactId).list();
            for (Pod pod : podList.getItems()) {
                logger.info("waitTillApplicationPodStarts(" + key + ", " + value + ") -> Pod : "
                        + pod.getMetadata().getName() + ", STATUS : " + KubernetesHelper.getPodStatus(pod)
                        + ", isPodReady : " + KubernetesHelper.isPodReady(pod));

                if (pod.getMetadata().getAnnotations().containsKey(key)) {
                    logger.info(pod.getMetadata().getName() + " is redeployed pod.");
                }
                if (pod.getMetadata().getAnnotations().containsKey(key)
                        && pod.getMetadata().getAnnotations().get(key).equalsIgnoreCase(value)
                        && KubernetesHelper.isPodReady(pod)) {
                    logger.info("OK  ... Pod wait over.");
                    TimeUnit.SECONDS.sleep(10);
                    return;
                }
            }
            nPolls++;
            TimeUnit.SECONDS.sleep(5);
        }
        throw new AssertionError(
                "Pod wait timeout! Could not find application pod for " + testsuiteRepositoryArtifactId);
    }

    protected void createViewRoleToServiceAccount() throws IOException, InterruptedException {
        exec("oc policy add-role-to-user view -z default");
    }

    protected void createOrReplaceConfigMap(String name, Map<String, String> data) {
        openShiftClient.configMaps().inNamespace(testsuiteNamespace).withName(name).edit().withData(data).done();
    }

    protected void createConfigMapResource(String name, Map<String, String> data) {
        if (openShiftClient.configMaps().inNamespace(testsuiteNamespace).withName(name).get() == null) {
            openShiftClient.configMaps().inNamespace(testsuiteNamespace).createNew().withNewMetadata()
                    .withName(name).endMetadata().withData(data).done();
        } else
            createOrReplaceConfigMap(name, data);
    }
}