org.apache.falcon.regression.core.util.InstanceUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.falcon.regression.core.util.InstanceUtil.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF 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 org.apache.falcon.regression.core.util;

import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonSyntaxException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.falcon.entity.v0.EntityType;
import org.apache.falcon.regression.core.bundle.Bundle;
import org.apache.falcon.regression.core.enumsAndConstants.ResponseErrors;
import org.apache.falcon.regression.core.helpers.entity.AbstractEntityHelper;
import org.apache.falcon.request.BaseRequest;
import org.apache.falcon.resource.APIResult;
import org.apache.falcon.resource.FeedInstanceResult;
import org.apache.falcon.resource.InstanceDependencyResult;
import org.apache.falcon.resource.InstancesResult;
import org.apache.falcon.resource.InstancesSummaryResult;
import org.apache.falcon.resource.SchedulableEntityInstance;
import org.apache.falcon.resource.TriageResult;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.security.authentication.client.AuthenticationException;
import org.apache.http.HttpResponse;
import org.apache.log4j.Logger;
import org.apache.oozie.client.BundleJob;
import org.apache.oozie.client.CoordinatorAction;
import org.apache.oozie.client.CoordinatorJob;
import org.apache.oozie.client.Job.Status;
import org.apache.oozie.client.OozieClient;
import org.apache.oozie.client.OozieClientException;
import org.apache.oozie.client.WorkflowJob;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.json.JSONException;
import org.testng.Assert;

import java.io.IOException;
import java.lang.reflect.Type;
import java.net.URISyntaxException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * util functions related to instanceTest.
 */
public final class InstanceUtil {

    public static final int INSTANCES_CREATED_TIMEOUT = OSUtil.IS_WINDOWS ? 20 : 10;
    private static final Logger LOGGER = Logger.getLogger(InstanceUtil.class);
    private static final EnumSet<Status> LIVE_STATUS = EnumSet.of(Status.RUNNING, Status.PREP, Status.SUCCEEDED,
            Status.SUSPENDED);

    private InstanceUtil() {
        throw new AssertionError("Instantiating utility class...");
    }

    public static APIResult sendRequestProcessInstance(String url, String user)
            throws IOException, URISyntaxException, AuthenticationException, InterruptedException {
        return hitUrl(url, Util.getMethodType(url), user);
    }

    public static APIResult hitUrl(String url, String method, String user)
            throws URISyntaxException, IOException, AuthenticationException, InterruptedException {
        BaseRequest request = new BaseRequest(url, method, user);
        HttpResponse response = request.run();
        String responseString = IOUtils.toString(response.getEntity().getContent(), "UTF-8");
        LOGGER.info("The web service response is:\n" + Util.prettyPrintXmlOrJson(responseString));
        APIResult result;
        if (url.contains("/summary/")) {
            result = new InstancesSummaryResult(APIResult.Status.FAILED, responseString);
        } else if (url.contains("/listing/")) {
            result = new FeedInstanceResult(APIResult.Status.FAILED, responseString);
        } else if (url.contains("instance/dependencies")) {
            result = new InstanceDependencyResult(APIResult.Status.FAILED, responseString);
        } else if (url.contains("instance/triage")) {
            result = new TriageResult(APIResult.Status.FAILED, responseString);
        } else {
            result = new InstancesResult(APIResult.Status.FAILED, responseString);
        }
        Assert.assertNotNull(result, "APIResult is null");
        for (ResponseErrors error : ResponseErrors.values()) {
            if (responseString.contains(error.getError())) {
                return result;
            }
        }
        final String[] errorStrings = { "(FEED) not found", "is beforePROCESS  start", "is after end date",
                "is after PROCESS's end", "is before PROCESS's  start", "is before the entity was scheduled", };
        for (String error : errorStrings) {
            if (responseString.contains(error)) {
                return result;
            }
        }
        try {
            result = new GsonBuilder().registerTypeAdapter(Date.class, new JsonDeserializer<Date>() {
                @Override
                public Date deserialize(JsonElement json, Type t, JsonDeserializationContext c) {
                    return new DateTime(json.getAsString()).toDate();
                }
            }).create().fromJson(responseString, getClassOfResult(url));
        } catch (JsonSyntaxException e) {
            Assert.fail("Not a valid json:\n" + responseString);
        }
        LOGGER.info("statusCode: " + response.getStatusLine().getStatusCode());
        LOGGER.info("message: " + result.getMessage());
        LOGGER.info("APIResult.Status: " + result.getStatus());
        return result;
    }

    /**
     * Returns API result class matching to API request url.
     */
    private static Class<? extends APIResult> getClassOfResult(String url) {
        final Class<? extends APIResult> classOfResult;
        if (url.contains("/listing/")) {
            classOfResult = FeedInstanceResult.class;
        } else if (url.contains("/summary/")) {
            classOfResult = InstancesSummaryResult.class;
        } else if (url.contains("instance/dependencies")) {
            classOfResult = InstanceDependencyResult.class;
        } else if (url.contains("instance/triage")) {
            classOfResult = TriageResult.class;
        } else {
            classOfResult = InstancesResult.class;
        }
        return classOfResult;
    }

    /**
     * Checks if API response reflects success and if it's instances match to expected status.
     *
     * @param instancesResult  - kind of response from API which should contain information about
     *                           instances
     * @param bundle           - bundle from which process instances are being analyzed
     * @param wfStatus - - expected status of instances
     */
    public static void validateSuccess(InstancesResult instancesResult, Bundle bundle,
            InstancesResult.WorkflowStatus wfStatus) {
        Assert.assertEquals(instancesResult.getStatus(), APIResult.Status.SUCCEEDED);
        Assert.assertEquals(instancesInResultWithStatus(instancesResult, wfStatus), bundle.getProcessConcurrency());
    }

    /**
     * Check the number of instances in response which have the same status as expected.
     *
     * @param instancesResult  kind of response from API which should contain information about
     *                         instances
     * @param workflowStatus   expected status of instances
     * @return number of instances which have expected status
     */
    public static int instancesInResultWithStatus(InstancesResult instancesResult,
            InstancesResult.WorkflowStatus workflowStatus) {
        InstancesResult.Instance[] instances = instancesResult.getInstances();
        LOGGER.info("instances: " + Arrays.toString(instances));
        List<InstancesResult.WorkflowStatus> statuses = new ArrayList<>();
        for (InstancesResult.Instance instance : instances) {
            LOGGER.info("instance: " + instance + " status = " + instance.getStatus());
            statuses.add(instance.getStatus());
        }
        return Collections.frequency(statuses, workflowStatus);
    }

    /**
     * Validates that response doesn't contains instances.
     * @param r response
     */
    public static void validateSuccessWOInstances(InstancesResult r) {
        AssertUtil.assertSucceeded(r);
        Assert.assertNull(r.getInstances(), "Unexpected :" + Arrays.toString(r.getInstances()));
    }

    /**
     * Validates that failed response contains specific error message.
     * @param instancesResult response
     * @param error expected error
     */
    public static void validateError(InstancesResult instancesResult, ResponseErrors error) {
        Assert.assertTrue(instancesResult.getMessage().contains(error.getError()),
                "Error should contains '" + error.getError() + "'");
    }

    /**
     * Checks that actual number of instances with different statuses are equal to expected number
     * of instances with matching statuses.
     *
     * @param instancesResult kind of response from API which should contain information about
     *                        instances <p/>
     *                        All parameters below reflect number of expected instances with some
     *                        kind of status.
     * @param totalCount      total number of instances.
     * @param runningCount    number of running instances.
     * @param suspendedCount  number of suspended instance.
     * @param waitingCount    number of waiting instance.
     * @param killedCount     number of killed instance.
     */
    public static void validateResponse(InstancesResult instancesResult, int totalCount, int runningCount,
            int suspendedCount, int waitingCount, int killedCount) {
        InstancesResult.Instance[] instances = instancesResult.getInstances();
        LOGGER.info("instances: " + Arrays.toString(instances));
        Assert.assertNotNull(instances, "instances should be not null");
        Assert.assertEquals(instances.length, totalCount, "Total Instances");
        List<InstancesResult.WorkflowStatus> statuses = new ArrayList<>();
        for (InstancesResult.Instance instance : instances) {
            final InstancesResult.WorkflowStatus status = instance.getStatus();
            LOGGER.info("status: " + status + ", instance: " + instance.getInstance());
            statuses.add(status);
        }
        Assert.assertEquals(Collections.frequency(statuses, InstancesResult.WorkflowStatus.RUNNING), runningCount,
                "Running Instances");
        Assert.assertEquals(Collections.frequency(statuses, InstancesResult.WorkflowStatus.SUSPENDED),
                suspendedCount, "Suspended Instances");
        Assert.assertEquals(Collections.frequency(statuses, InstancesResult.WorkflowStatus.WAITING), waitingCount,
                "Waiting Instances");
        Assert.assertEquals(Collections.frequency(statuses, InstancesResult.WorkflowStatus.KILLED), killedCount,
                "Killed Instances");
    }

    /**
     * Retrieves workflow IDs from every instances from response.
     * @param instancesResult response
     * @return list of workflow IDs
     */
    public static List<String> getWorkflowJobIds(InstancesResult instancesResult) {
        InstancesResult.Instance[] instances = instancesResult.getInstances();
        LOGGER.info("Instances: " + Arrays.toString(instances));
        Assert.assertNotNull(instances, "Instances should be not null");
        List<String> wfIds = new ArrayList<>();
        for (InstancesResult.Instance instance : instances) {
            LOGGER.warn(String.format("instance: %s, status: %s, logs : %s", instance, instance.getStatus(),
                    instance.getLogFile()));
            if (instance.getStatus().name().equals("RUNNING") || instance.getStatus().name().equals("SUCCEEDED")) {
                wfIds.add(instance.getLogFile());
            }
            if (instance.getStatus().name().equals("KILLED") || instance.getStatus().name().equals("WAITING")) {
                Assert.assertNull(instance.getLogFile());
            }
        }
        return wfIds;
    }

    /**
     * Checks that expected number of failed instances matches actual number of failed ones.
     *
     * @param instancesResult kind of response from API which should contain information about
     *                        instances.
     * @param failCount number of instances which should be failed.
     */
    public static void validateFailedInstances(InstancesResult instancesResult, int failCount) {
        AssertUtil.assertSucceeded(instancesResult);
        int counter = 0;
        for (InstancesResult.Instance oneInstance : instancesResult.getInstances()) {
            if (oneInstance.getStatus() == InstancesResult.WorkflowStatus.FAILED) {
                counter++;
            }
        }
        Assert.assertEquals(counter, failCount,
                "Actual number of failed instances does not " + "match to expected number of failed instances.");
    }

    /**
     * Gets process workflows by given statuses.
     * @param oozieClient oozie client of cluster where process is running
     * @param processName process name
     * @param statuses statuses workflows will be selected by
     * @return list of matching workflows
     * @throws OozieClientException
     */
    public static List<String> getWorkflows(OozieClient oozieClient, String processName,
            WorkflowJob.Status... statuses) throws OozieClientException {
        String bundleID = OozieUtil.getBundles(oozieClient, processName, EntityType.PROCESS).get(0);
        List<String> workflowJobIds = OozieUtil.getWorkflowJobs(oozieClient, bundleID);

        List<String> toBeReturned = new ArrayList<>();
        for (String jobId : workflowJobIds) {
            WorkflowJob wfJob = oozieClient.getJobInfo(jobId);
            LOGGER.info("wfJob.getId(): " + wfJob.getId() + " wfJob.getStartTime(): " + wfJob.getStartTime()
                    + "jobId: " + jobId + "  wfJob.getStatus(): " + wfJob.getStatus());
            if (statuses.length == 0 || Arrays.asList(statuses).contains(wfJob.getStatus())) {
                toBeReturned.add(jobId);
            }
        }
        return toBeReturned;
    }

    public static boolean isWorkflowRunning(OozieClient oozieClient, String workflowID)
            throws OozieClientException {
        WorkflowJob.Status status = oozieClient.getJobInfo(workflowID).getStatus();
        return status == WorkflowJob.Status.RUNNING;
    }

    public static void areWorkflowsRunning(OozieClient oozieClient, List<String> workflowIds, int totalWorkflows,
            int runningWorkflows, int killedWorkflows, int succeededWorkflows) throws OozieClientException {
        if (totalWorkflows != -1) {
            Assert.assertEquals(workflowIds.size(), totalWorkflows);
        }
        final List<WorkflowJob.Status> statuses = new ArrayList<>();
        for (String wfId : workflowIds) {
            final WorkflowJob.Status status = oozieClient.getJobInfo(wfId).getStatus();
            LOGGER.info("wfId: " + wfId + " status: " + status);
            statuses.add(status);
        }
        if (runningWorkflows != -1) {
            Assert.assertEquals(Collections.frequency(statuses, WorkflowJob.Status.RUNNING), runningWorkflows,
                    "Number of running jobs doesn't match.");
        }
        if (killedWorkflows != -1) {
            Assert.assertEquals(Collections.frequency(statuses, WorkflowJob.Status.KILLED), killedWorkflows,
                    "Number of killed jobs doesn't match.");
        }
        if (succeededWorkflows != -1) {
            Assert.assertEquals(Collections.frequency(statuses, WorkflowJob.Status.SUCCEEDED), succeededWorkflows,
                    "Number of succeeded jobs doesn't match.");
        }
    }

    public static List<CoordinatorAction> getProcessInstanceList(OozieClient oozieClient, String processName,
            EntityType entityType) throws OozieClientException {
        String coordId = OozieUtil.getLatestCoordinatorID(oozieClient, processName, entityType);
        //String coordId = getDefaultCoordinatorFromProcessName(processName);
        LOGGER.info("default coordID: " + coordId);
        return oozieClient.getCoordJobInfo(coordId).getActions();
    }

    public static int getInstanceCountWithStatus(OozieClient oozieClient, String processName,
            CoordinatorAction.Status status, EntityType entityType) throws OozieClientException {
        List<CoordinatorAction> coordActions = getProcessInstanceList(oozieClient, processName, entityType);
        List<CoordinatorAction.Status> statuses = new ArrayList<>();
        for (CoordinatorAction action : coordActions) {
            statuses.add(action.getStatus());
        }
        return Collections.frequency(statuses, status);
    }

    /**
     * Retrieves status of one instance.
     *
     * @param oozieClient     - server from which instance status will be retrieved.
     * @param processName    - name of process which mentioned instance belongs to.
     * @param bundleNumber   - ordinal number of one of the bundle which are related to that
     *                         process.
     * @param instanceNumber - ordinal number of instance which state will be returned.
     * @return - state of mentioned instance.
     * @throws OozieClientException
     */
    public static CoordinatorAction.Status getInstanceStatus(OozieClient oozieClient, String processName,
            int bundleNumber, int instanceNumber) throws OozieClientException {
        String bundleID = OozieUtil.getSequenceBundleID(oozieClient, processName, EntityType.PROCESS, bundleNumber);
        if (StringUtils.isEmpty(bundleID)) {
            return null;
        }
        String coordID = OozieUtil.getDefaultCoordIDFromBundle(oozieClient, bundleID);
        if (StringUtils.isEmpty(coordID)) {
            return null;
        }
        CoordinatorJob coordInfo = oozieClient.getCoordJobInfo(coordID);
        if (coordInfo == null) {
            return null;
        }
        LOGGER.info("coordInfo = " + coordInfo);
        List<CoordinatorAction> actions = coordInfo.getActions();
        if (actions.size() == 0) {
            return null;
        }
        LOGGER.info("actions = " + actions);
        return actions.get(instanceNumber).getStatus();
    }

    /**
     * Forms and sends process instance request based on url of action to be performed and it's
     * parameters.
     *
     * @param colo - servers on which action should be performed
     * @param user - whose credentials will be used for this action
     * @return result from API
     */
    public static APIResult createAndSendRequestProcessInstance(String url, String params, String colo, String user)
            throws IOException, URISyntaxException, AuthenticationException, InterruptedException {
        if (params != null && !colo.equals("")) {
            url = url + params + "&" + colo.substring(1);
        } else if (params != null) {
            url = url + params;
        } else {
            url = url + colo;
        }
        return sendRequestProcessInstance(url, user);
    }

    public static org.apache.oozie.client.WorkflowJob.Status getInstanceStatusFromCoord(OozieClient oozieClient,
            String coordID, int instanceNumber) throws OozieClientException {
        CoordinatorJob coordInfo = oozieClient.getCoordJobInfo(coordID);
        String jobId = coordInfo.getActions().get(instanceNumber).getExternalId();
        LOGGER.info("jobId = " + jobId);
        if (jobId == null) {
            return null;
        }
        WorkflowJob actionInfo = oozieClient.getJobInfo(jobId);
        return actionInfo.getStatus();
    }

    public static List<String> getInputFoldersForInstanceForReplication(OozieClient oozieClient, String coordID,
            int instanceNumber) throws OozieClientException {
        CoordinatorAction x = oozieClient.getCoordActionInfo(coordID + "@" + instanceNumber);
        String jobId = x.getExternalId();
        WorkflowJob wfJob = oozieClient.getJobInfo(jobId);
        return getReplicationFolderFromInstanceRunConf(wfJob.getConf());
    }

    private static List<String> getReplicationFolderFromInstanceRunConf(String runConf) {
        String conf;
        conf = runConf.substring(runConf.indexOf("falconInPaths</name>") + 20);
        conf = conf.substring(conf.indexOf("<value>") + 7);
        conf = conf.substring(0, conf.indexOf("</value>"));
        return new ArrayList<>(Arrays.asList(conf.split(",")));
    }

    public static int getInstanceRunIdFromCoord(OozieClient oozieClient, String coordID, int instanceNumber)
            throws OozieClientException {
        CoordinatorJob coordInfo = oozieClient.getCoordJobInfo(coordID);
        WorkflowJob actionInfo = oozieClient.getJobInfo(coordInfo.getActions().get(instanceNumber).getExternalId());
        return actionInfo.getRun();
    }

    public static int checkIfFeedCoordExist(AbstractEntityHelper helper, String feedName, String coordType)
            throws OozieClientException {
        LOGGER.info("feedName: " + feedName);
        int numberOfCoord = 0;

        final OozieClient oozieClient = helper.getOozieClient();
        if (OozieUtil.getBundles(oozieClient, feedName, EntityType.FEED).size() == 0) {
            return 0;
        }
        List<String> bundleIds = OozieUtil.getBundles(oozieClient, feedName, EntityType.FEED);
        LOGGER.info("bundleIds: " + bundleIds);

        for (String bundleId : bundleIds) {
            LOGGER.info("bundleId: " + bundleId);
            OozieUtil.waitForCoordinatorJobCreation(oozieClient, bundleId);
            List<CoordinatorJob> coords = OozieUtil.getBundleCoordinators(oozieClient, bundleId);
            LOGGER.info("coords: " + coords);
            for (CoordinatorJob coord : coords) {
                if (coord.getAppName().contains(coordType)) {
                    numberOfCoord++;
                }
            }
        }
        return numberOfCoord;
    }

    public static List<CoordinatorAction> getProcessInstanceListFromAllBundles(OozieClient oozieClient,
            String processName, EntityType entityType) throws OozieClientException {
        List<CoordinatorAction> list = new ArrayList<>();
        final List<String> bundleIds = OozieUtil.getBundles(oozieClient, processName, entityType);
        LOGGER.info("bundle size for process is " + bundleIds.size());
        for (String bundleId : bundleIds) {
            BundleJob bundleInfo = oozieClient.getBundleJobInfo(bundleId);
            List<CoordinatorJob> coordJobs = bundleInfo.getCoordinators();
            LOGGER.info("number of coordJobs in bundle " + bundleId + "=" + coordJobs.size());
            for (CoordinatorJob coordJob : coordJobs) {
                List<CoordinatorAction> actions = oozieClient.getCoordJobInfo(coordJob.getId()).getActions();
                LOGGER.info("number of actions in coordinator " + coordJob.getId() + " is " + actions.size());
                list.addAll(actions);
            }
        }
        String coordId = OozieUtil.getLatestCoordinatorID(oozieClient, processName, entityType);
        LOGGER.info("default coordID: " + coordId);
        return list;
    }

    public static String getOutputFolderForInstanceForReplication(OozieClient oozieClient, String coordID,
            int instanceNumber) throws OozieClientException {
        CoordinatorJob coordInfo = oozieClient.getCoordJobInfo(coordID);
        final CoordinatorAction coordAction = coordInfo.getActions().get(instanceNumber);
        final String actionConf = oozieClient.getJobInfo(coordAction.getExternalId()).getConf();
        return getReplicatedFolderFromInstanceRunConf(actionConf);
    }

    private static String getReplicatedFolderFromInstanceRunConf(String runConf) {
        String inputPathExample = getReplicationFolderFromInstanceRunConf(runConf).get(0);
        String postFix = inputPathExample.substring(inputPathExample.length() - 7, inputPathExample.length());
        return getReplicatedFolderBaseFromInstanceRunConf(runConf) + "/" + postFix;
    }

    public static String getOutputFolderBaseForInstanceForReplication(OozieClient oozieClient, String coordID,
            int instanceNumber) throws OozieClientException {
        CoordinatorJob coordInfo = oozieClient.getCoordJobInfo(coordID);
        final CoordinatorAction coordAction = coordInfo.getActions().get(instanceNumber);
        final String actionConf = oozieClient.getJobInfo(coordAction.getExternalId()).getConf();
        return getReplicatedFolderBaseFromInstanceRunConf(actionConf);
    }

    private static String getReplicatedFolderBaseFromInstanceRunConf(String runConf) {
        String conf = runConf.substring(runConf.indexOf("distcpTargetPaths</name>") + 24);
        conf = conf.substring(conf.indexOf("<value>") + 7);
        conf = conf.substring(0, conf.indexOf("</value>"));
        return conf;
    }

    /**
     * Waits till supplied number of instances of process/feed reach expected state during
     * specific time.
     *
     * @param client             oozie client to retrieve info about instances
     * @param entityName         name of feed or process
     * @param instancesNumber    instance number for which we wait to reach the required status
     * @param expectedStatus     expected status we are waiting for
     * @param entityType         type of entity - feed or process expected
     * @param totalMinutesToWait time in minutes for which instance state should be polled
     * @throws OozieClientException
     */
    public static void waitTillInstanceReachState(OozieClient client, String entityName, int instancesNumber,
            CoordinatorAction.Status expectedStatus, EntityType entityType, int totalMinutesToWait)
            throws OozieClientException {
        String filter;
        // get the bundle ids
        if (entityType.equals(EntityType.FEED)) {
            filter = "name=FALCON_FEED_" + entityName;
        } else {
            filter = "name=FALCON_PROCESS_" + entityName;
        }
        List<BundleJob> bundleJobs = new ArrayList<>();
        for (int retries = 0; retries < 20; ++retries) {
            bundleJobs = OozieUtil.getBundles(client, filter, 0, 10);
            if (bundleJobs.size() > 0) {
                break;
            }
            TimeUtil.sleepSeconds(5);
        }
        if (bundleJobs.size() == 0) {
            Assert.fail("Could not retrieve bundles");
        }
        List<String> bundleIds = OozieUtil.getBundleIds(bundleJobs);
        Collections.sort(bundleIds, Collections.reverseOrder());
        String coordId = null;
        for (String bundleId : bundleIds) {
            LOGGER.info(String.format("Using bundle %s", bundleId));
            final Status status = client.getBundleJobInfo(bundleId).getStatus();
            Assert.assertTrue(LIVE_STATUS.contains(status),
                    String.format("Bundle job %s is should be prep/running but is %s", bundleId, status));
            OozieUtil.waitForCoordinatorJobCreation(client, bundleId);
            List<CoordinatorJob> coords = client.getBundleJobInfo(bundleId).getCoordinators();
            List<String> cIds = new ArrayList<>();
            if (entityType == EntityType.PROCESS) {
                for (CoordinatorJob coord : coords) {
                    cIds.add(coord.getId());
                }
                coordId = OozieUtil.getMinId(cIds);
                break;
            } else {
                for (CoordinatorJob coord : coords) {
                    if (coord.getAppName().contains("FEED_REPLICATION")) {
                        cIds.add(coord.getId());
                    }
                }
                if (!cIds.isEmpty()) {
                    coordId = cIds.get(0);
                    break;
                }
            }
        }
        Assert.assertNotNull(coordId, "Coordinator id not found");
        LOGGER.info(String.format("Using coordinator id: %s", coordId));
        int maxTries = 50;
        int totalSleepTime = totalMinutesToWait * 60;
        int sleepTime = totalSleepTime / maxTries;
        LOGGER.info(String.format("Sleep for %d seconds", sleepTime));
        for (int i = 0; i < maxTries; i++) {
            LOGGER.info(String.format("Try %d of %d", (i + 1), maxTries));
            CoordinatorJob coordinatorJob = client.getCoordJobInfo(coordId);
            final Status coordinatorStatus = coordinatorJob.getStatus();
            if (expectedStatus != CoordinatorAction.Status.TIMEDOUT) {
                Assert.assertTrue(LIVE_STATUS.contains(coordinatorStatus), String
                        .format("Coordinator %s should be running/prep but is %s.", coordId, coordinatorStatus));
            }
            List<CoordinatorAction> coordinatorActions = coordinatorJob.getActions();
            int instanceWithStatus = 0;
            for (CoordinatorAction coordinatorAction : coordinatorActions) {
                LOGGER.info(String.format("Coordinator Action %s status is %s on oozie %s",
                        coordinatorAction.getId(), coordinatorAction.getStatus(), client.getOozieUrl()));
                if (expectedStatus == coordinatorAction.getStatus()) {
                    instanceWithStatus++;
                }
            }
            if (instanceWithStatus >= instancesNumber) {
                return;
            } else {
                TimeUtil.sleepSeconds(sleepTime);
            }
        }
        Assert.fail("expected state of instance was never reached");
    }

    /**
     * Waits till supplied number of instances of process/feed reach expected state during
     * specific time.
     *
     * @param client           oozie client to retrieve info about instances
     * @param entityName       name of feed or process
     * @param numberOfInstance number of instances which status we are waiting for
     * @param expectedStatus   expected status we are waiting for
     * @param entityType       type of entity - feed or process expected
     */
    public static void waitTillInstanceReachState(OozieClient client, String entityName, int numberOfInstance,
            CoordinatorAction.Status expectedStatus, EntityType entityType) throws OozieClientException {
        int totalMinutesToWait = getMinutesToWait(entityType, expectedStatus);
        waitTillInstanceReachState(client, entityName, numberOfInstance, expectedStatus, entityType,
                totalMinutesToWait);
    }

    /**
     * Generates time which is presumably needed for process/feed instances to reach particular
     * state.
     * Feed instances are running faster then process, so feed timeouts are less then process.
     *
     * @param entityType     type of entity which instances status we are waiting for
     * @param expectedStatus expected status we are waiting for
     * @return minutes to wait for expected status
     */
    public static int getMinutesToWait(EntityType entityType, CoordinatorAction.Status expectedStatus) {
        switch (expectedStatus) {
        case RUNNING:
            if (entityType == EntityType.PROCESS) {
                return OSUtil.IS_WINDOWS ? 20 : 10;
            } else if (entityType == EntityType.FEED) {
                return OSUtil.IS_WINDOWS ? 10 : 5;
            }
        case WAITING:
            return OSUtil.IS_WINDOWS ? 6 : 3;
        case SUCCEEDED:
            if (entityType == EntityType.PROCESS) {
                return OSUtil.IS_WINDOWS ? 25 : 15;
            } else if (entityType == EntityType.FEED) {
                return OSUtil.IS_WINDOWS ? 20 : 10;
            }
        case KILLED:
        case TIMEDOUT:
            return OSUtil.IS_WINDOWS ? 40 : 20;
        default:
            return OSUtil.IS_WINDOWS ? 30 : 15;
        }
    }

    /**
     * Waits till instances of specific job will be created during specific time.
     * Use this method directly in unusual test cases where timeouts are different from trivial.
     * In other cases use waitTillInstancesAreCreated(OozieClient,String,int)
     *
     * @param oozieClient oozie client of the cluster on which job is running
     * @param entity      definition of entity which describes job
     * @param bundleSeqNo bundle number if update has happened.
     * @throws OozieClientException
     */
    public static void waitTillInstancesAreCreated(OozieClient oozieClient, String entity, int bundleSeqNo,
            int totalMinutesToWait) throws OozieClientException {
        String entityName = Util.readEntityName(entity);
        EntityType type = Util.getEntityType(entity);
        String bundleID = OozieUtil.getSequenceBundleID(oozieClient, entityName, type, bundleSeqNo);
        String coordID = OozieUtil.getDefaultCoordIDFromBundle(oozieClient, bundleID);
        for (int sleepCount = 0; sleepCount < totalMinutesToWait; sleepCount++) {
            CoordinatorJob coordInfo = oozieClient.getCoordJobInfo(coordID);

            if (coordInfo.getActions().size() > 0) {
                break;
            }
            LOGGER.info("Coord " + coordInfo.getId() + " still doesn't have " + "instance created on oozie: "
                    + oozieClient.getOozieUrl());
            TimeUtil.sleepSeconds(5);
        }
    }

    /**
     * Waits till instances of specific job will be created during timeout.
     * Timeout is common for most of usual test cases.
     *
     * @param oozieClient  oozieClient of cluster job is running on
     * @param entity      definition of entity which describes job
     * @param bundleSeqNo bundle number if update has happened.
     * @throws OozieClientException
     */
    public static void waitTillInstancesAreCreated(OozieClient oozieClient, String entity, int bundleSeqNo)
            throws OozieClientException {
        int sleep = INSTANCES_CREATED_TIMEOUT * 60 / 5;
        waitTillInstancesAreCreated(oozieClient, entity, bundleSeqNo, sleep);
    }

    /**
     * Asserts instances of specific job will be present for given instanceTime.
     *
     * @param instancesResult  InstanceDependencyResult
     * @param oozieClient  oozieClient of cluster job is running on
     * @param bundleID     bundleId of job
     * @param time  instanceTime.
     * @throws JSONException
     * @throws ParseException
     */
    public static void assertProcessInstances(InstanceDependencyResult instancesResult, OozieClient oozieClient,
            String bundleID, String time) throws OozieClientException, ParseException, JSONException {
        List<String> inputPath = new ArrayList<>();
        List<String> outputPath = new ArrayList<>();
        SchedulableEntityInstance[] instances = instancesResult.getDependencies();
        LOGGER.info("instances: " + Arrays.toString(instances));
        Assert.assertNotNull(instances, "instances should be not null");
        for (SchedulableEntityInstance instance : instances) {
            Assert.assertNotNull(instance.getCluster());
            Assert.assertNotNull(instance.getEntityName());
            Assert.assertNotNull(instance.getEntityType());
            Assert.assertNotNull(instance.getInstanceTime());
            Assert.assertNotNull(instance.getTags());
            if (instance.getTags().equals("Input")) {
                inputPath.add(new DateTime(instance.getInstanceTime(), DateTimeZone.UTC).toString());
            }
            if (instance.getTags().equals("Output")) {
                outputPath.add(new DateTime(instance.getInstanceTime(), DateTimeZone.UTC).toString());
            }
        }

        List<String> inputActual = getMinuteDatesToPath(
                inputPath.get(inputPath.indexOf(Collections.min(inputPath))),
                inputPath.get(inputPath.indexOf(Collections.max(inputPath))), 5);
        List<String> outputActual = getMinuteDatesToPath(
                outputPath.get(outputPath.indexOf(Collections.min(outputPath))),
                outputPath.get(outputPath.indexOf(Collections.max(outputPath))), 5);

        Configuration conf = OozieUtil.getProcessConf(oozieClient, bundleID, time);
        Assert.assertNotNull(conf, "Configuration should not be null");
        List<String> inputExp = Arrays.asList(conf.get("inputData").split(","));
        List<String> outputExp = Arrays.asList(conf.get("outputData").split(","));

        Assert.assertTrue(matchList(inputExp, inputActual), " Inputs dont match");
        Assert.assertTrue(matchList(outputExp, outputActual), " Outputs dont match");

    }

    /**
     * Returns list of path based on given start and end time.
     *
     * @param startOozieDate  start date
     * @param endOozieDate    end date
     * @param minuteSkip      difference  between paths
     * @throws ParseException
     */
    public static List<String> getMinuteDatesToPath(String startOozieDate, String endOozieDate, int minuteSkip)
            throws ParseException {
        String myFormat = "yyyy'-'MM'-'dd'T'HH':'mm'Z'";
        String userFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'";
        return TimeUtil.getMinuteDatesOnEitherSide(TimeUtil.parseDate(startOozieDate, myFormat, userFormat),
                TimeUtil.parseDate(endOozieDate, myFormat, userFormat), minuteSkip);
    }

    /**
     * Parses date from one format to another.
     *
     * @param oozieDate  input date
     * @throws ParseException
     */
    public static String getParsedDates(String oozieDate) throws ParseException {
        String myFormat = "yyyy'-'MM'-'dd'T'HH':'mm'Z'";
        String userFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'";
        return TimeUtil.parseDate(oozieDate, myFormat, userFormat);
    }

    /**
     * Asserts Whether two list are equal or not.
     *
     * @param firstList  list<String> to be comapred
     * @param secondList  list<String> to be compared
     */
    public static boolean matchList(List<String> firstList, List<String> secondList) {
        Collections.sort(firstList);
        Collections.sort(secondList);
        if (firstList.size() != secondList.size()) {
            return false;
        }
        for (int index = 0; index < firstList.size(); index++) {
            if (!firstList.get(index).contains(secondList.get(index))) {
                return false;
            }
        }
        return true;
    }

    /**
     * Asserts instanceDependencyResult of specific job for a given feed.
     *
     * @param instancesResult  InstanceDependencyResult
     * @param processName  process name for given bundle
     * @param tag     Input/Output
     * @param expectedInstances  instance for given instanceTime.
     * @throws ParseException
     */
    public static void assertFeedInstances(InstanceDependencyResult instancesResult, String processName, String tag,
            List<String> expectedInstances) throws ParseException {
        List<String> actualInstances = new ArrayList<>();
        SchedulableEntityInstance[] instances = instancesResult.getDependencies();
        LOGGER.info("instances: " + Arrays.toString(instances));
        Assert.assertNotNull(instances, "instances should be not null");
        for (SchedulableEntityInstance instance : instances) {
            Assert.assertNotNull(instance.getCluster());
            Assert.assertNotNull(instance.getEntityName());
            Assert.assertNotNull(instance.getEntityType());
            Assert.assertNotNull(instance.getInstanceTime());
            Assert.assertNotNull(instance.getTags());
            Assert.assertTrue(instance.getEntityType().toString().equals("PROCESS"), "Type should be PROCESS");
            Assert.assertTrue(instance.getEntityName().equals(processName), "Expected name is : " + processName);
            Assert.assertTrue(instance.getTags().equals(tag));
            actualInstances
                    .add(getParsedDates(new DateTime(instance.getInstanceTime(), DateTimeZone.UTC).toString()));
        }

        Set<String> expectedInstancesSet = new HashSet<>(expectedInstances);
        Set<String> actualInstancesSet = new HashSet<>(actualInstances);
        Assert.assertEquals(expectedInstancesSet, actualInstancesSet, "Instances don't match");
    }
}