com.vmware.admiral.test.integration.BaseIntegrationSupportIT.java Source code

Java tutorial

Introduction

Here is the source code for com.vmware.admiral.test.integration.BaseIntegrationSupportIT.java

Source

/*
 * Copyright (c) 2016 VMware, Inc. All Rights Reserved.
 *
 * This product is licensed to you under the Apache License, Version 2.0 (the "License").
 * You may not use this product except in compliance with the License.
 *
 * This product may include a number of subcomponents with separate copyright notices
 * and license terms. Your use of these subcomponents is subject to the terms and
 * conditions of the subcomponent's license, as noted in the LICENSE file.
 */

package com.vmware.admiral.test.integration;

import static org.junit.Assert.assertNotNull;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

import org.apache.commons.lang3.StringUtils;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.rules.Timeout;

import com.vmware.admiral.common.util.AssertUtil;
import com.vmware.admiral.compute.ContainerHostService;
import com.vmware.admiral.compute.ContainerHostService.ContainerHostSpec;
import com.vmware.admiral.compute.endpoint.EndpointAdapterService;
import com.vmware.admiral.request.ContainerHostRemovalTaskFactoryService;
import com.vmware.admiral.request.ContainerHostRemovalTaskService.ContainerHostRemovalTaskState;
import com.vmware.admiral.request.RequestStatusFactoryService;
import com.vmware.admiral.request.RequestStatusService.RequestStatus;
import com.vmware.admiral.service.common.TaskServiceDocument;
import com.vmware.admiral.test.integration.SimpleHttpsClient.HttpMethod;
import com.vmware.admiral.test.integration.SimpleHttpsClient.HttpResponse;
import com.vmware.photon.controller.model.constants.PhotonModelConstants.EndpointType;
import com.vmware.photon.controller.model.resources.ComputeService;
import com.vmware.photon.controller.model.resources.ComputeService.ComputeState;
import com.vmware.photon.controller.model.resources.ComputeService.ComputeStateWithDescription;
import com.vmware.photon.controller.model.resources.ComputeService.PowerState;
import com.vmware.photon.controller.model.resources.EndpointService.EndpointState;
import com.vmware.photon.controller.model.tasks.ResourceEnumerationTaskService;
import com.vmware.photon.controller.model.tasks.ResourceEnumerationTaskService.ResourceEnumerationTaskState;
import com.vmware.xenon.common.Operation;
import com.vmware.xenon.common.ServiceDocument;
import com.vmware.xenon.common.TaskState;
import com.vmware.xenon.common.TestLogger;
import com.vmware.xenon.common.UriUtils;
import com.vmware.xenon.common.Utils;

public abstract class BaseIntegrationSupportIT {
    private static final String TEST_INTEGRATION_PROPERTIES_FILE_PATH = "test.integration.properties";
    private static final String TEST_DCP_URL_PROP_NAME = "test.dcp.url";
    private static final String TEST_DCP_HOST_PROP_NAME = "test.dcp.host";
    private static final String TEST_DCP_PORT_PROP_NAME = "test.dcp.port";

    protected static final int STATE_CHANGE_WAIT_POLLING_RETRY_COUNT = Integer
            .getInteger("test.state.change.wait.retry.count", 300);
    protected static final int TASK_CHANGE_WAIT_POLLING_RETRY_COUNT = Integer
            .getInteger("test.task.change.wait.retry.count", 600);
    protected static final int STATE_CHANGE_WAIT_POLLING_PERIOD_MILLIS = Integer
            .getInteger("test.state.change.wait.period.millis", 1000);

    private static final String ENDPOINT_ID = "endpoint";
    private static final String TENANT_LINKS_KEY = "test.tenant.links";
    public static final String SUFFIX = "bel10";

    private static final Properties testProperties = loadTestProperties();

    protected static final Queue<ServiceDocument> documentsForDeletionAfterClass = new LinkedBlockingQueue<>();
    protected static final Queue<ServiceDocument> documentsForDeletion = new LinkedBlockingQueue<>();
    protected final TestLogger logger;

    private List<String> tenantLinks;

    protected BaseIntegrationSupportIT() {
        logger = new TestLogger(getClass());
    }

    @Rule
    public Timeout globalTimeout = Timeout.seconds(TimeUnit.MINUTES.toSeconds(15));

    @BeforeClass
    public static void baseBeforeClass() {
        // Allow "Host" header to be passed
        // http://stackoverflow.com/questions/7648872/can-i-override-the-host-header-where-using-javas-httpurlconnection-class
        System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
    }

    @AfterClass
    public static void baseAfterClass() throws Exception {
        try {
            while (!documentsForDeletionAfterClass.isEmpty()) {
                delete(documentsForDeletionAfterClass.poll());
            }
        } catch (Exception e) {
            documentsForDeletionAfterClass.clear();
            e.printStackTrace();
        }
    }

    @After
    public void baseTearDown() throws Exception {
        try {
            while (!documentsForDeletion.isEmpty()) {
                ServiceDocument documentToDelete = documentsForDeletion.poll();
                logger.info("Cleanup: deleting %s", documentToDelete.documentSelfLink);
                delete(documentToDelete);
            }
        } catch (Exception e) {
            documentsForDeletion.clear();
            e.printStackTrace();
        }
    }

    protected static void cleanUpAfterClass(ServiceDocument document) {
        documentsForDeletionAfterClass.add(document);
    }

    protected static void cleanUpAfter(ServiceDocument document) {
        documentsForDeletion.add(document);
    }

    protected static String getBaseUrl() {
        // if a dynmic port is used, build the URL from the host and port parts
        String port = testProperties.getProperty(TEST_DCP_PORT_PROP_NAME);

        if (port != null) {
            String host = testProperties.getProperty(TEST_DCP_HOST_PROP_NAME, "127.0.0.1");
            String baseUrl = String.format("http://%s:%s", host, port);
            return baseUrl;
        }

        return testProperties.getProperty(TEST_DCP_URL_PROP_NAME);
    }

    protected static String getTestRequiredProp(String key) {
        String property = testProperties.getProperty(key);
        if (property == null || property.isEmpty()) {
            throw new IllegalStateException(String.format("Property '%s' is required", key));
        }
        return property;
    }

    protected static String getTestProp(String key) {
        return testProperties.getProperty(key);
    }

    protected static String getTestProp(String key, String defaultValue) {
        return testProperties.getProperty(key, defaultValue);
    }

    private static Properties loadTestProperties() {
        Properties properties = new Properties();
        try {
            properties.load(BaseIntegrationSupportIT.class.getClassLoader()
                    .getResourceAsStream("integration-test.properties"));
            File systemConfiguredTestPropertiesFile = getSystemConfiguredTestPropertiesFile();
            if (systemConfiguredTestPropertiesFile == null) {
                System.out.println(String.format("Required system property: %s is not provided",
                        TEST_INTEGRATION_PROPERTIES_FILE_PATH));
            } else {
                loadProperties(properties, systemConfiguredTestPropertiesFile);
            }
            String baseDcpUrl = System.getProperty(TEST_DCP_URL_PROP_NAME);
            if (baseDcpUrl != null) {
                properties.put(TEST_DCP_URL_PROP_NAME, baseDcpUrl);
            }
        } catch (Exception e) {
            throw new RuntimeException("Error during test properties loading.", e);
        }
        return properties;
    }

    private static File getSystemConfiguredTestPropertiesFile() {
        String integrationProperties = System.getProperty(TEST_INTEGRATION_PROPERTIES_FILE_PATH);
        if (integrationProperties == null) {
            return null;
        }
        return new File(integrationProperties);
    }

    protected static Properties loadProperties(Properties properties, File propertyFile) {
        try (FileInputStream inStream = new FileInputStream(propertyFile)) {
            properties.load(inStream);
        } catch (IOException e) {
            throw new IllegalArgumentException(String.format("Error loading %s", propertyFile.getAbsolutePath()),
                    e);
        }
        return properties;
    }

    protected static String buildServiceUri(String... paths) {
        StringBuilder url = new StringBuilder();
        for (String path : paths) {
            url.append(UriUtils.normalizeUriPath(path));
        }
        return url.toString();
    }

    protected static void delete(ServiceDocument document) throws Exception {
        delete(document.documentSelfLink);
    }

    protected static void delete(String documentSelfLink) throws Exception {
        sendRequest(HttpMethod.DELETE, documentSelfLink, Utils.toJson(new ServiceDocument()));
    }

    protected static <T extends ServiceDocument> T getDocument(String seflLink, Class<? extends T> type)
            throws Exception {
        String body = sendRequest(HttpMethod.GET, seflLink, null);
        if (body == null || body.isEmpty()) {
            return null;
        }
        return Utils.fromJson(body, type);
    }

    public static enum TestDocumentLifeCycle {
        FOR_DELETE, FOR_DELETE_AFTER_CLASS, NO_DELETE
    }

    protected static <T extends ServiceDocument> T postDocument(String fabricLink, T document) throws Exception {
        return postDocument(fabricLink, document, TestDocumentLifeCycle.FOR_DELETE);
    }

    @SuppressWarnings("unchecked")
    protected static <T extends ServiceDocument> T postDocument(String fabricLink, T document,
            TestDocumentLifeCycle documentLifeCycle) throws Exception {
        if (document.documentSelfLink != null && !document.documentSelfLink.isEmpty()) {
            String servicePathUrl = buildServiceUri(fabricLink, extractId(document.documentSelfLink));
            String body = sendRequest(HttpMethod.GET, servicePathUrl, null);
            if (body != null && !body.isEmpty()) {
                delete(servicePathUrl);
            }
        }
        String body = sendRequest(HttpMethod.POST, fabricLink, Utils.toJson(document));
        if (body == null) {
            return null;
        }
        T doc = (T) Utils.fromJson(body, document.getClass());
        switch (documentLifeCycle) {
        case FOR_DELETE:
            cleanUpAfter(doc);
            break;
        case FOR_DELETE_AFTER_CLASS:
            cleanUpAfterClass(doc);
            break;
        default:
            break;
        }
        return doc;
    }

    protected static ComputeState addHost(ComputeState computeState) throws Exception {
        if (computeState.id != null) {
            String documentSelfLink = buildServiceUri(ComputeService.FACTORY_LINK, computeState.id);
            String body = sendRequest(HttpMethod.GET, documentSelfLink, null);
            if (body != null && !body.isEmpty()) {
                delete(documentSelfLink);
            }
        }

        ContainerHostSpec hostSpec = new ContainerHostSpec();
        hostSpec.hostState = computeState;
        hostSpec.acceptCertificate = true;

        HttpResponse httpResponse = SimpleHttpsClient.execute(HttpMethod.PUT,
                getBaseUrl() + buildServiceUri(ContainerHostService.SELF_LINK), Utils.toJson(hostSpec));

        if (HttpURLConnection.HTTP_NO_CONTENT != httpResponse.statusCode) {
            throw new IllegalArgumentException("Add host failed with status code: " + httpResponse.statusCode);
        }

        List<String> headers = httpResponse.headers.get(Operation.LOCATION_HEADER);
        String computeStateLink = headers.get(0);

        waitForStateChange(computeStateLink, t -> {
            ComputeStateWithDescription host = Utils.fromJson(t, ComputeStateWithDescription.class);
            return host.powerState.equals(PowerState.ON);
        });

        return getDocument(computeStateLink, ComputeState.class);
    }

    protected void removeHost(ComputeState computeState) throws Exception {
        logger.info("---------- Request remove host. --------");
        if (computeState != null && computeState.id != null) {
            String documentSelfLink = buildServiceUri(ComputeService.FACTORY_LINK, computeState.id);
            String body = sendRequest(HttpMethod.GET, documentSelfLink, null);
            if (body != null && !body.isEmpty()) {
                // host is found, remove it
                ContainerHostRemovalTaskState state = new ContainerHostRemovalTaskState();
                state.resourceLinks = Collections.singleton(computeState.documentSelfLink);
                state = postDocument(ContainerHostRemovalTaskFactoryService.SELF_LINK, state);

                assertNotNull("task is null", state);
                String taskSelfLink = state.documentSelfLink;
                assertNotNull("task self link is missing", taskSelfLink);
                waitForTaskToComplete(taskSelfLink);
            } else {
                logger.info("Docker host not found. Skipping removal");
            }
        }
    }

    protected static String sendRequest(HttpMethod method, String link, String body) throws Exception {
        HttpResponse httpResponse = SimpleHttpsClient.execute(method, getBaseUrl() + buildServiceUri(link), body);
        return httpResponse.responseBody;
    }

    protected void waitForTaskToComplete(final String documentSelfLink) throws Exception {
        TaskState taskState = null;
        boolean error = false;
        for (int i = 0; i < TASK_CHANGE_WAIT_POLLING_RETRY_COUNT; i++) {
            Thread.sleep(STATE_CHANGE_WAIT_POLLING_PERIOD_MILLIS);
            String body = sendRequest(HttpMethod.GET, documentSelfLink, null);

            taskState = null;
            if (body != null) {
                taskState = Utils.getJsonMapValue(body, TaskServiceDocument.FIELD_NAME_TASK_INFO, TaskState.class);
            }

            if (TaskState.isFinished(taskState)) {
                return;
            }

            RequestStatus requestStatus = getDocument(
                    UriUtils.buildUriPath(RequestStatusFactoryService.SELF_LINK, extractId(documentSelfLink)),
                    RequestStatus.class);
            if (requestStatus != null) {
                logger.info("~~~~~~~~~ Request %s status progress: %s. Progress by component: %s   ~~~~~~~",
                        documentSelfLink, requestStatus.progress, requestStatus.requestProgressByComponent);
            }

            if (TaskState.isCancelled(taskState) || TaskState.isFailed(taskState)) {
                error = true;
                break;
            }
        }

        String failure = "";
        if (taskState != null && taskState.failure != null) {
            failure = "Failure: " + taskState.failure.message;
        }

        if (error) {
            throw new IllegalStateException(
                    String.format("Failed with error waiting for %s to succeed. %s", documentSelfLink, failure));
        } else {
            throw new RuntimeException(
                    String.format("Timeout waiting for %s to succeed. %s", documentSelfLink, failure));
        }
    }

    protected static void waitForStateChange(final String documentSelfLink, Predicate<String> predicate)
            throws Exception {

        String body = null;
        for (int i = 0; i < STATE_CHANGE_WAIT_POLLING_RETRY_COUNT; i++) {
            Thread.sleep(STATE_CHANGE_WAIT_POLLING_PERIOD_MILLIS);
            body = sendRequest(HttpMethod.GET, documentSelfLink, null);
            if (predicate.test(body)) {
                return;
            }
        }

        throw new RuntimeException(String.format("Failed waiting for state change"));
    }

    protected static void waitForStatusCode(URI uri, int expectedStatusCode) throws Exception {
        waitForStatusCode(uri, expectedStatusCode, STATE_CHANGE_WAIT_POLLING_RETRY_COUNT);
    }

    protected static void waitForStatusCode(URI uri, int expectedStatusCode, int count) throws Exception {
        waitForStatusCode(uri, Collections.emptyMap(), expectedStatusCode, count);
    }

    protected static void waitForStatusCode(URI uri, Map<String, String> headers, int expectedStatusCode, int count)
            throws Exception {

        for (int i = 0; i < count; i++) {
            Thread.sleep(STATE_CHANGE_WAIT_POLLING_PERIOD_MILLIS);
            try {
                HttpResponse httpResponse = SimpleHttpsClient.execute(HttpMethod.GET, uri.toString(), null, headers,
                        getUnsecuredSSLSocketFactory());
                if (expectedStatusCode == httpResponse.statusCode) {
                    return;
                }
            } catch (Exception x) {
                // failed - keep waiting
            }
        }

        throw new RuntimeException(
                String.format("Failed waiting for status code %s at %s", expectedStatusCode, uri));
    }

    protected static String getJsonValue(String body, String fieldName) {
        JsonObject jo = new JsonParser().parse(body).getAsJsonObject();
        JsonElement je = jo.get(fieldName);
        if (je == null) {
            return null;
        }

        return je.getAsString();
    }

    protected static String extractId(String link) {
        AssertUtil.assertNotNull(link, "link");
        if (link.endsWith(UriUtils.URI_PATH_CHAR)) {
            link = link.substring(0, link.length() - 1);
        }
        return link.substring(link.lastIndexOf(UriUtils.URI_PATH_CHAR) + 1);
    }

    /**
     * Get a property designated by path from a root object
     * <p/>
     * This implementation assumes the object is a nested map of maps until the leaf object is found
     *
     * @param object
     * @param propertyPathElements
     * @return
     */
    @SuppressWarnings("unchecked")
    protected <T> T getNestedPropertyByPath(Object object, String... propertyPathElements) {
        List<String> pathParts = new LinkedList<>(Arrays.asList(propertyPathElements));

        while ((object instanceof Map) && (!pathParts.isEmpty())) {
            String pathPart = pathParts.remove(0);

            Map<String, Object> map = (Map<String, Object>) object;
            object = map.get(pathPart);
        }

        return (T) object;
    }

    protected String getResourceContaining(Collection<String> links, String pattern) {
        for (String resourceLink : links) {
            if (resourceLink.contains(pattern)) {
                return resourceLink;
            }
        }

        return null;
    }

    protected EndpointState createEndpoint(EndpointType endpointType, TestDocumentLifeCycle documentLifeCycle)
            throws Exception {
        EndpointState endpoint = new EndpointState();
        endpoint.endpointType = endpointType.name();
        endpoint.name = name(endpointType, ENDPOINT_ID, SUFFIX);
        endpoint.tenantLinks = getTenantLinks();
        endpoint.endpointProperties = new HashMap<>();
        extendEndpoint(endpoint);

        return postDocument(EndpointAdapterService.SELF_LINK, endpoint, documentLifeCycle);
    }

    protected void triggerAndWaitForEndpointEnumeration(EndpointState endpoint) throws Exception {
        ComputeState parentCompute = getDocument(endpoint.computeLink, ComputeState.class);

        ResourceEnumerationTaskState enumTask = new ResourceEnumerationTaskState();
        enumTask.parentComputeLink = endpoint.computeLink;
        enumTask.resourcePoolLink = endpoint.resourcePoolLink;
        enumTask.adapterManagementReference = parentCompute.adapterManagementReference;
        enumTask.tenantLinks = endpoint.tenantLinks;

        ResourceEnumerationTaskState returnedState = postDocument(ResourceEnumerationTaskService.FACTORY_LINK,
                enumTask);

        waitForTaskToComplete(returnedState.documentSelfLink);
    }

    protected abstract EndpointType getEndpointType();

    protected abstract void extendEndpoint(EndpointState endpoint);

    protected String name(EndpointType endpointType, String prefix, String suffix) {
        return String.format("%s-%s-%s", prefix, endpointType.name(), suffix);
    }

    protected List<String> getTenantLinks() {
        if (this.tenantLinks == null) {
            String tenantLinkProp = getTestProp(TENANT_LINKS_KEY, "/tenants/admiral");
            String[] values = StringUtils.split(tenantLinkProp, ',');

            List<String> result = new LinkedList<>();
            for (int i = 0; i < values.length; i++) {
                result.add(values[i].trim());
            }
            this.tenantLinks = result;
        }
        return tenantLinks;
    }

    protected static SSLSocketFactory getUnsecuredSSLSocketFactory()
            throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, new TrustManager[] { UnsecuredX509TrustManager.getInstance() }, null);
        return context.getSocketFactory();
    }

    private static class UnsecuredX509TrustManager implements X509TrustManager {

        private static UnsecuredX509TrustManager instance;

        private UnsecuredX509TrustManager() {
        }

        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        public static UnsecuredX509TrustManager getInstance() {
            if (instance == null) {
                instance = new UnsecuredX509TrustManager();
            }

            return instance;
        }
    }
}