package com.rackspacecloud.blueflood;
import com.rackspacecloud.blueflood.service.Configuration;
import com.rackspacecloud.blueflood.service.CoreConfig;
import org.apache.commons.io.IOUtils;
import org.codehaus.jackson.map.ObjectMapper;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;
/**
* A collection of static fields & methods which are useful in testing blueflood. Most of these methods are involved with
* generating data for the JSON API.
*/
public class TestUtils {
public static final String ERROR_TITLE = "The following errors have been encountered:";
public static final String PAST_COLLECTION_TIME_REGEX = ".* is more than '" + Configuration.getInstance().getLongProperty( CoreConfig.BEFORE_CURRENT_COLLECTIONTIME_MS ) + "' milliseconds into the past\\.$";
public static final String FUTURE_COLLECTION_TIME_REGEX = ".* is more than '" + Configuration.getInstance().getLongProperty( CoreConfig.AFTER_CURRENT_COLLECTIONTIME_MS ) + "' milliseconds into the future\\.$";
public static final String NO_TENANT_ID_REGEX = ".* No tenantId is provided for the metric\\.";
private static final ObjectMapper mapper = new ObjectMapper();
private static final String TIMESTAMP = "\"%TIMESTAMP%\"";
private static final String POSTFIX = "%POSTFIX%";
private static final Random random = new Random();
// making this just a static class, no instantiations
private TestUtils() {}
/**
* Generate multi-tenant metrics data using:
* <li>current timestamp
* <li>tenant ids of tenantOne and tennatTwo
* <li>random metric name postfix
*
* @return
* @throws Exception
*/
public static String generateMultitenantJSONMetricsData() throws Exception {
long collectionTime = System.currentTimeMillis();
List<Map<String, Object>> dataOut = new ArrayList<Map<String, Object>>();
for (Map<String, Object> stringObjectMap : generateMetricsData( "", collectionTime )) {
stringObjectMap.put("tenantId", "tenantOne");
dataOut.add(stringObjectMap);
}
for (Map<String, Object> stringObjectMap : generateMetricsData( "", collectionTime )) {
stringObjectMap.put("tenantId", "tenantTwo");
dataOut.add(stringObjectMap);
}
return mapper.writeValueAsString(dataOut);
}
/**
* Generate a single metric data using:
* <li>current timestamp
* <li>random metric name postfix
*
* @return
* @throws Exception
*/
public static String generateJSONMetricsData() throws Exception {
return generateJSONMetricsData( System.currentTimeMillis() );
}
/**
* Generate single metric data using:
* <li> provided timestamp
* <li> provided metric name postfix
*
* @param metricPostfix
* @param collectionTime
* @return
* @throws Exception
*/
public static String generateJSONMetricsData( String metricPostfix, long collectionTime ) throws Exception {
StringWriter writer = new StringWriter();
mapper.writeValue(writer, generateMetricsData( metricPostfix, collectionTime ));
return writer.toString();
}
/**
* Generate single metric data using:
* <li> current timestamp
* <li> provided metric name postfix
*
* @param metricPostfix
* @return
* @throws Exception
*/
public static String generateJSONMetricsData( String metricPostfix ) throws Exception {
return generateJSONMetricsData( metricPostfix, System.currentTimeMillis() );
}
/**
* get raw json payload from file at payloadPath
* @param payloadPath
* @return String of json payload
*/
public static String getJsonFromFile(String payloadPath) throws IOException {
Reader reader = new InputStreamReader(TestUtils.class.getClassLoader().getResourceAsStream( payloadPath ) );
StringWriter writer = new StringWriter();
IOUtils.copy( reader, writer );
IOUtils.closeQuietly( reader );
return writer.toString();
}
/**
* Given a payloadPath string pointing to a blueflood ingestion payload, replace all POSTFIX patterns with:
* <li> current timestamp
* <li> provided metric name postfix
*
* @param payloadPath
* @param postfix
* @return json string of payload with current TIMESTAMP & POSTFIX patterns replaced
* @throws IOException
*/
public static String getJsonFromFile(String payloadPath, String postfix ) throws IOException {
return getJsonFromFile(payloadPath, System.currentTimeMillis(), postfix);
}
/**
* Given a payloadPath string pointing to a blueflood ingestion payload, replace all TIMESTAMP & POSTFIX patterns with:
* <li> provided timestamp
* <li> provided metric name postfix
*
* @param payloadPath
* @param timestamp
* @param postfix
* @return json string of payload with TIMESTAMP & POSTFIX patterns replaced
* @throws IOException
*/
public static String getJsonFromFile(String payloadPath, long timestamp, String postfix ) throws IOException {
String json = getJsonFromFile(payloadPath);
// update timestamp
json = updateTimeStampJson(json, TIMESTAMP, timestamp);
// append random number to metric name
json = json.replace( POSTFIX, postfix );
return json;
}
public static String updateTimeStampJson(String json, String timestampName, long timestampMillis) {
// JSON might have several entries for the same metric. If they have the same timestamp, they could overwrite
// each other. Not using sleep() here to increment the time as to not have to deal with
// InterruptedException. Rather, incrementing the time by 1 ms.
long increment = 0;
while( json.contains( timestampName ) ) {
json = json.replaceFirst( timestampName, Long.toString( timestampMillis + increment++ ) );
}
return json;
}
/**
* Generate single metric data using:
* <li> provided timestamp
* <li> random generated metric name postfix
*
* @param collectionTime
* @return
* @throws Exception
*/
private static String generateJSONMetricsData( long collectionTime ) throws Exception {
StringWriter writer = new StringWriter();
mapper.writeValue(writer, generateMetricsData( "", collectionTime ));
return writer.toString();
}
/**
* Generate a single metric data having string value using:
* <li> provided timestamp
* <li> random generated metric name postfix
*
* @param collectionTime
* @return
* @throws Exception
*/
public static String generateJSONMetricsDataWithStringValue( long collectionTime ) throws Exception {
StringWriter writer = new StringWriter();
mapper.writeValue(writer, generateMetricsDataWithStringValue("", collectionTime));
return writer.toString();
}
/**
* Generate a single metric data having numeric string value using:
* <li> provided timestamp
* <li> random generated metric name postfix
*
* @param collectionTime
* @return
* @throws Exception
*/
public static String generateJSONMetricsDataWithNumericStringValue( long collectionTime ) throws Exception {
StringWriter writer = new StringWriter();
mapper.writeValue(writer, generateMetricsDataWithNumericStringValue("", collectionTime));
return writer.toString();
}
/**
* Generate metric data having string and boolean values using:
* <li> provided timestamp
* <li> random generated metric name postfix
*
* @param collectionTime
* @return
* @throws Exception
*/
public static String generateJSONMetricsDataWithAllWrongTypes( boolean generateRandomTenant, long collectionTime ) throws Exception {
StringWriter writer = new StringWriter();
mapper.writeValue(writer, generateMetricsDataWithAllWrongTypes("", generateRandomTenant, collectionTime));
return writer.toString();
}
/**
* Generate metric data having string and boolean values using:
* <li> provided timestamp
* <li> random generated metric name postfix
*
* @param collectionTime
* @return
* @throws Exception
*/
public static String generateJSONMetricsDataWithPartialWrongTypes( boolean generateRandomTenant, long collectionTime ) throws Exception {
StringWriter writer = new StringWriter();
mapper.writeValue(writer, generateMetricsDataWithPartialWrongTypes("", generateRandomTenant, collectionTime));
return writer.toString();
}
/**
* Returns a list of list of maps which represent metrics, each metric uses:
* <li> provided timestamp
* <li> metric name postfix
*
* @param metricPostfix
* @param collectionTime
* @return
*/
private static List<Map<String, Object>> generateMetricsData( String metricPostfix, long collectionTime ) {
List<Map<String, Object>> metricsList = new ArrayList<Map<String, Object>>();
// Long metric value
Map<String, Object> testMetric = new TreeMap<String, Object>();
testMetric.put("metricName", "mzord.duration" + metricPostfix );
testMetric.put("ttlInSeconds", 1234566);
testMetric.put("unit", "milliseconds");
testMetric.put("metricValue", Long.MAX_VALUE);
testMetric.put("collectionTime", collectionTime );
metricsList.add(testMetric);
// status metric value, 0 is up, 1 is down
testMetric = new TreeMap<String, Object>();
testMetric.put("metricName", "mzord.status" + metricPostfix );
testMetric.put("ttlInSeconds", 1234566);
testMetric.put("unit", "unknown");
testMetric.put("metricValue", 0);
testMetric.put("collectionTime", collectionTime );
metricsList.add(testMetric);
// null metric value. This shouldn't be in the final list of metrics because we ignore null valued metrics.
testMetric = new TreeMap<String, Object>();
testMetric.put("metricName", "mzord.hipster" + metricPostfix );
testMetric.put("ttlInSeconds", 1234566);
testMetric.put("unit", "unknown");
testMetric.put("metricValue", null);
testMetric.put("collectionTime", collectionTime );
metricsList.add(testMetric);
return metricsList;
}
public static List<Map<String, Object>> generateMetricsDataWithAllWrongTypes( String metricPostfix, boolean generateRandomTenant, long collectionTime ) {
List<Map<String, Object>> metricsList = new ArrayList<Map<String, Object>>();
// String metric value
Map<String, Object> testMetric = new TreeMap<String, Object>();
if ( generateRandomTenant ) {
testMetric.put("tenantId", random.nextInt());
}
testMetric.put("metricName", "mzord.string.metric" + metricPostfix);
testMetric.put("ttlInSeconds", 1234566);
testMetric.put("unit", "milliseconds");
testMetric.put("metricValue", "random_string");
testMetric.put("collectionTime", collectionTime);
metricsList.add(testMetric);
// String numeric metric value
testMetric = new TreeMap<String, Object>();
if ( generateRandomTenant ) {
testMetric.put("tenantId", random.nextInt());
}
testMetric.put("metricName", "mzord.string.numeric.metric" + metricPostfix);
testMetric.put("ttlInSeconds", 1234566);
testMetric.put("unit", "milliseconds");
testMetric.put("metricValue", "666");
testMetric.put("collectionTime", collectionTime);
metricsList.add(testMetric);
// boolean metric value
testMetric = new TreeMap<String, Object>();
if ( generateRandomTenant ) {
testMetric.put("tenantId", random.nextInt());
}
testMetric.put("metricName", "mzord.boolean.metric" + metricPostfix);
testMetric.put("ttlInSeconds", 1234566);
testMetric.put("unit", "milliseconds");
testMetric.put("metricValue", true);
testMetric.put("collectionTime", collectionTime);
metricsList.add(testMetric);
return metricsList;
}
private static List<Map<String, Object>> generateMetricsDataWithPartialWrongTypes( String metricPostfix, boolean generateRandomTenant, long collectionTime ) {
List<Map<String, Object>> metricsList = generateMetricsDataWithAllWrongTypes(metricPostfix, generateRandomTenant, collectionTime);
// add a few valid ones
Map<String, Object> testMetric = new TreeMap<String, Object>();
if ( generateRandomTenant ) {
testMetric.put("tenantId", random.nextInt());
}
testMetric.put("metricName", "mzord.small.numeric.metric" + metricPostfix);
testMetric.put("ttlInSeconds", 1234566);
testMetric.put("unit", "milliseconds");
testMetric.put("metricValue", 123);
testMetric.put("collectionTime", collectionTime);
metricsList.add(testMetric);
// numeric metrics
testMetric = new TreeMap<String, Object>();
if ( generateRandomTenant ) {
testMetric.put("tenantId", random.nextInt());
}
testMetric.put("metricName", "mzord.another.numeric.metric" + metricPostfix);
testMetric.put("ttlInSeconds", 1234566);
testMetric.put("unit", "ounces");
testMetric.put("metricValue", 4525);
testMetric.put("collectionTime", collectionTime);
metricsList.add(testMetric);
return metricsList;
}
private static List<Map<String, Object>> generateMetricsDataWithStringValue( String metricPostfix, long collectionTime ) {
List<Map<String, Object>> metricsList = new ArrayList<Map<String, Object>>();
// String metric value
Map<String, Object> testMetric = new TreeMap<String, Object>();
testMetric.put("metricName", "mzord.string.metric" + metricPostfix);
testMetric.put("ttlInSeconds", 1234566);
testMetric.put("unit", "milliseconds");
testMetric.put("metricValue", "random_string");
testMetric.put("collectionTime", collectionTime);
metricsList.add(testMetric);
return metricsList;
}
private static List<Map<String, Object>> generateMetricsDataWithNumericStringValue( String metricPostfix, long collectionTime ) {
List<Map<String, Object>> metricsList = new ArrayList<Map<String, Object>>();
// Numeric string metric value
Map<String, Object> testMetric = new TreeMap<String, Object>();
testMetric.put("metricName", "mzord.numeric.string.metric" + metricPostfix);
testMetric.put("ttlInSeconds", 1234566);
testMetric.put("unit", "milliseconds");
testMetric.put("metricValue", "2000");
testMetric.put("collectionTime", collectionTime);
metricsList.add(testMetric);
return metricsList;
}
}