| /* |
| * 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.usergrid.settings |
| |
| import java.io.{PrintWriter, FileOutputStream} |
| import java.net.URLDecoder |
| import java.util |
| import java.util.concurrent.TimeUnit |
| import java.util.concurrent.atomic.AtomicInteger |
| import javax.xml.bind.DatatypeConverter |
| import io.gatling.http.Predef._ |
| import io.gatling.core.Predef._ |
| import io.gatling.http.config.HttpProtocolBuilder |
| import org.apache.usergrid.datagenerators.FeederGenerator |
| import org.apache.usergrid.enums._ |
| import org.apache.usergrid.helpers.Utils |
| import scala.collection.mutable |
| |
| object Settings { |
| |
| def initStrSetting(cfg: String): String = { |
| val setting = System.getProperty(cfg) |
| |
| if (setting != null) setting else ConfigProperties.getDefault(cfg).toString |
| } |
| |
| def initBoolSetting(cfg: String): Boolean = { |
| val strSetting = System.getProperty(cfg) |
| val default:Boolean = ConfigProperties.getDefault(cfg).asInstanceOf[Boolean] |
| |
| if (strSetting != null) { |
| if (default) // default is true |
| strSetting.toLowerCase != "false" |
| else // default is false |
| strSetting.toLowerCase == "true" |
| } else { |
| default |
| } |
| } |
| |
| def initIntSetting(cfg: String): Int = { |
| val integerSetting:Integer = Integer.getInteger(cfg) |
| |
| if (integerSetting != null) |
| integerSetting.toInt |
| else |
| ConfigProperties.getDefault(cfg).asInstanceOf[Int] |
| } |
| |
| def initLongSetting(cfg: String): Long = { |
| val longSetting:java.lang.Long = java.lang.Long.getLong(cfg) |
| |
| if (longSetting != null) |
| longSetting.toLong |
| else |
| ConfigProperties.getDefault(cfg).asInstanceOf[Long] |
| } |
| |
| // load configuration settings via property or default |
| val org = initStrSetting(ConfigProperties.Org) |
| val app = initStrSetting(ConfigProperties.App) |
| val allApps: Boolean = app == "*" |
| val adminUser = initStrSetting(ConfigProperties.AdminUser) |
| val adminPassword = initStrSetting(ConfigProperties.AdminPassword) |
| |
| private val cfgBaseUrl = initStrSetting(ConfigProperties.BaseUrl) |
| val baseUrl = if (cfgBaseUrl.takeRight(1) == "/") cfgBaseUrl.dropRight(1) else cfgBaseUrl |
| def orgUrl(org: String): String = { |
| baseUrl + "/" + org |
| } |
| def appUrl(app: String): String = { |
| orgUrl(org) + "/" + app |
| } |
| val managementUrl = baseUrl + "/management/organizations" + org |
| val baseOrgUrl = orgUrl(org) |
| val baseAppUrl = appUrl(app) |
| |
| private def httpConf(baseUrl: String): HttpProtocolBuilder = { |
| http |
| .baseURL(baseUrl) |
| .connection("keep-alive") |
| .extraInfoExtractor { |
| i => |
| if (Settings.printFailedRequests && i.status == io.gatling.core.result.message.KO) { |
| println(s"==============") |
| println(s"Request: ${i.request.getMethod} ${i.request.getUrl}") |
| println(s"body:") |
| println(s" ${i.request.getStringData}") |
| println(s"==============") |
| println(s"Response: ${i.response.statusCode.getOrElse(-1)}") |
| println(s"body:") |
| println(s" ${i.response.body.string}") |
| println(s"==============") |
| } |
| Nil |
| } |
| } |
| val httpOrgConf: HttpProtocolBuilder = httpConf(baseOrgUrl) |
| val httpAppConf: HttpProtocolBuilder = httpConf(baseAppUrl) |
| val authType = initStrSetting(ConfigProperties.AuthType) |
| val tokenType = initStrSetting(ConfigProperties.TokenType) |
| |
| val skipSetup:Boolean = initBoolSetting(ConfigProperties.SkipSetup) |
| val createOrg:Boolean = !skipSetup && initBoolSetting(ConfigProperties.CreateOrg) |
| val createApp:Boolean = !skipSetup && initBoolSetting(ConfigProperties.CreateApp) |
| val loadEntities:Boolean = !skipSetup && initBoolSetting(ConfigProperties.LoadEntities) |
| val sandboxCollection:Boolean = initBoolSetting(ConfigProperties.SandboxCollection) |
| val scenarioType = initStrSetting(ConfigProperties.ScenarioType) |
| |
| val rampUsers:Int = initIntSetting(ConfigProperties.RampUsers) |
| val constantUsersPerSec:Int = initIntSetting(ConfigProperties.ConstantUsersPerSec) // users to add per second during constant injection |
| val constantUsersDuration:Int = initIntSetting(ConfigProperties.ConstantUsersDuration) // number of seconds |
| val totalUsers:Int = rampUsers + (constantUsersPerSec * constantUsersDuration) |
| val userSeed:Int = initIntSetting(ConfigProperties.UserSeed) |
| val appUser = initStrSetting(ConfigProperties.AppUser) |
| val appUserPassword = initStrSetting(ConfigProperties.AppUserPassword) |
| |
| // val appUserBase64 = Base64.getEncoder.encodeToString((appUser + ":" + appUserPassword).getBytes(StandardCharsets.UTF_8)) |
| val appUserBase64: String = DatatypeConverter.printBase64Binary((appUser + ":" + appUserPassword).getBytes("UTF-8")) |
| |
| val totalNumEntities:Int = initIntSetting(ConfigProperties.NumEntities) |
| val numDevices:Int = initIntSetting(ConfigProperties.NumDevices) |
| |
| val collection = initStrSetting(ConfigProperties.Collection) |
| val baseCollectionUrl = baseAppUrl + "/" + collection |
| |
| val rampTime:Int = initIntSetting(ConfigProperties.RampTime) // in seconds |
| val throttle:Int = initIntSetting(ConfigProperties.Throttle) // in seconds |
| val holdDuration:Int = initIntSetting(ConfigProperties.HoldDuration) // in seconds |
| |
| // Geolocation settings |
| val centerLatitude:Double = 37.442348 // latitude of center point |
| val centerLongitude:Double = -122.138268 // longitude of center point |
| val userLocationRadius:Double = 32000 // location of requesting user in meters |
| val geoSearchRadius:Int = 8000 // search area in meters |
| |
| // Push Notification settings |
| val pushNotifier = initStrSetting(ConfigProperties.PushNotifier) |
| val pushProvider = initStrSetting(ConfigProperties.PushProvider) |
| |
| // Large Entity Collection settings |
| val entityPrefix = initStrSetting(ConfigProperties.EntityPrefix) |
| val entityType = initStrSetting(ConfigProperties.EntityType) // basic/trivial/? |
| val overallEntitySeed = initIntSetting(ConfigProperties.EntitySeed) |
| val searchLimit:Int = initIntSetting(ConfigProperties.SearchLimit) |
| val searchQuery = initStrSetting(ConfigProperties.SearchQuery) |
| val endConditionType = initStrSetting(ConfigProperties.EndConditionType) |
| val endMinutes:Int = initIntSetting(ConfigProperties.EndMinutes) |
| val endRequestCount:Int = initIntSetting(ConfigProperties.EndRequestCount) |
| |
| // Org creation fields |
| private val cfgOrgCreationUsername = initStrSetting(ConfigProperties.OrgCreationUsername) |
| private val cfgOrgCreationEmail = initStrSetting(ConfigProperties.OrgCreationEmail) |
| private val cfgOrgCreationName = initStrSetting(ConfigProperties.OrgCreationName) |
| val orgCreationUsername = if (cfgOrgCreationUsername == "") org.concat("_admin") else cfgOrgCreationUsername |
| val orgCreationEmail = if (cfgOrgCreationEmail == "") orgCreationUsername.concat("@usergrid.com") else cfgOrgCreationEmail |
| val orgCreationName = if (cfgOrgCreationName == "") orgCreationUsername else cfgOrgCreationName |
| val orgCreationPassword = initStrSetting(ConfigProperties.OrgCreationPassword) |
| |
| val retryCount:Int = initIntSetting(ConfigProperties.RetryCount) |
| val laterThanTimestamp:Long = initLongSetting(ConfigProperties.LaterThanTimestamp) |
| val entityProgressCount:Long = initLongSetting(ConfigProperties.EntityProgressCount) |
| private val logEntityProgress: Boolean = entityProgressCount > 0L |
| val injectionList = initStrSetting(ConfigProperties.InjectionList) |
| val printFailedRequests:Boolean = initBoolSetting(ConfigProperties.PrintFailedRequests) |
| val getViaQuery:Boolean = initBoolSetting(ConfigProperties.GetViaQuery) |
| private val queryParamConfig = initStrSetting(ConfigProperties.QueryParams) |
| val queryParamMap: Map[String,String] = mapFromQueryParamConfigString(queryParamConfig) |
| |
| val multiPropertyPrefix = initStrSetting(ConfigProperties.MultiPropertyPrefix) |
| val multiPropertyCount:Int = initIntSetting(ConfigProperties.MultiPropertyCount) |
| val multiPropertySizeInK:Int = initIntSetting(ConfigProperties.MultiPropertySizeInK) |
| val entityNumberProperty = initStrSetting(ConfigProperties.EntityNumberProperty) |
| |
| // Entity update |
| val updateProperty = initStrSetting(ConfigProperties.UpdateProperty) |
| val updateValue = initStrSetting(ConfigProperties.UpdateValue) |
| val updateBody = Utils.toJSONStr(Map(updateProperty -> updateValue)) |
| |
| // Entity workers |
| private val cfgEntityWorkerCount:Int = initIntSetting(ConfigProperties.EntityWorkerCount) |
| private val cfgEntityWorkerNum:Int = initIntSetting(ConfigProperties.EntityWorkerNum) |
| val useWorkers:Boolean = cfgEntityWorkerCount > 1 && cfgEntityWorkerNum >= 1 && cfgEntityWorkerNum <= cfgEntityWorkerCount |
| val entityWorkerCount:Int = if (useWorkers) cfgEntityWorkerCount else 1 |
| val entityWorkerNum:Int = if (useWorkers) cfgEntityWorkerNum else 1 |
| |
| // if only one worker system, these numbers will still be fine |
| private val entitiesPerWorkerFloor:Int = totalNumEntities / entityWorkerCount |
| private val leftOver:Int = totalNumEntities % entityWorkerCount // will be 0 if only one worker |
| private val extraEntity:Int = if (entityWorkerNum <= leftOver) 1 else 0 |
| private val zeroBasedWorkerNum:Int = entityWorkerNum - 1 |
| val entitySeed:Int = overallEntitySeed + zeroBasedWorkerNum * entitiesPerWorkerFloor + (if (extraEntity == 1) zeroBasedWorkerNum else leftOver) |
| val numEntities:Int = entitiesPerWorkerFloor + extraEntity |
| |
| // UUID log file, have to go through this because creating a csv feeder with an invalid csv file fails at maven compile time |
| private val dummyTestCsv = ConfigProperties.getDefault(ConfigProperties.UuidFilename).toString |
| private val dummyAuditCsv = ConfigProperties.getDefault(ConfigProperties.AuditUuidFilename).toString |
| private val dummyAuditFailedCsv = ConfigProperties.getDefault(ConfigProperties.FailedUuidFilename).toString |
| private val dummyCaptureCsv = "/tmp/notused.csv" |
| |
| private val uuidFilename = initStrSetting(ConfigProperties.UuidFilename) |
| private val auditUuidFilename = initStrSetting(ConfigProperties.AuditUuidFilename) |
| private val failedUuidFilename = initStrSetting(ConfigProperties.FailedUuidFilename) |
| |
| // feeds require valid files, even if test won't be run |
| val feedUuids = scenarioType match { |
| case ScenarioType.UuidRandomInfinite => true |
| case _ => false |
| } |
| val feedUuidFilename = scenarioType match { |
| case ScenarioType.UuidRandomInfinite => uuidFilename |
| case _ => dummyTestCsv |
| } |
| if (feedUuids && feedUuidFilename == dummyTestCsv) { |
| println("Scenario requires CSV file containing UUIDs") |
| System.exit(1) |
| } |
| |
| val feedAuditUuids = scenarioType match { |
| case ScenarioType.AuditVerifyCollectionEntities => true |
| case _ => false |
| } |
| val feedAuditUuidFilename = scenarioType match { |
| case ScenarioType.AuditVerifyCollectionEntities => auditUuidFilename |
| case _ => dummyAuditCsv |
| } |
| if (feedAuditUuids && feedAuditUuidFilename == dummyAuditCsv) { |
| println("Scenario requires CSV file containing audit UUIDs") |
| System.exit(1) |
| } |
| |
| val captureUuidFilename = scenarioType match { |
| case ScenarioType.LoadEntities => uuidFilename |
| case ScenarioType.GetByNameSequential => uuidFilename |
| case _ => dummyCaptureCsv // won't write to this file |
| } |
| val captureUuids = if (captureUuidFilename == dummyCaptureCsv) false |
| else scenarioType match { |
| case ScenarioType.LoadEntities => true |
| case ScenarioType.GetByNameSequential => true |
| case _ => false |
| } |
| |
| val captureAuditUuidFilename = scenarioType match { |
| case ScenarioType.AuditGetCollectionEntities => auditUuidFilename |
| case ScenarioType.AuditVerifyCollectionEntities => failedUuidFilename |
| case _ => dummyCaptureCsv // won't write to this file |
| } |
| if (scenarioType == ScenarioType.AuditGetCollectionEntities && captureAuditUuidFilename == dummyCaptureCsv) { |
| println("Scenario requires CSV file location to capture audit UUIDs") |
| System.exit(1) |
| } |
| val captureAuditUuids = (scenarioType == ScenarioType.AuditGetCollectionEntities) || |
| (scenarioType == ScenarioType.AuditVerifyCollectionEntities && captureAuditUuidFilename != dummyAuditFailedCsv) |
| |
| /* |
| println(s"feedUuids=$feedUuids") |
| println(s"feedUuidFilename=$feedUuidFilename") |
| println(s"feedAuditUuids=$feedAuditUuids") |
| println(s"feedAuditUuidFilename=$feedAuditUuidFilename") |
| println(s"captureUuids=$captureUuids") |
| println(s"captureUuidFilename=$captureUuidFilename") |
| println(s"captureAuditUuids=$captureAuditUuids") |
| println(s"captureAuditUuidFilename=$captureAuditUuidFilename") |
| */ |
| |
| val purgeUsers:Int = initIntSetting(ConfigProperties.PurgeUsers) |
| |
| private var uuidMap: Map[Int, String] = Map() |
| private var entityCounter: Long = 0 |
| private var lastEntityCountPrinted: Long = 0 |
| def addUuid(num: Int, uuid: String): Unit = { |
| if (captureUuids) { |
| uuidMap.synchronized { |
| uuidMap += (num -> uuid) |
| entityCounter += 1 |
| if (logEntityProgress && (entityCounter >= lastEntityCountPrinted + entityProgressCount)) { |
| println(s"Entity: $entityCounter") |
| lastEntityCountPrinted = entityCounter |
| } |
| } |
| } |
| // println(s"UUID: ${name},${uuid}") |
| } |
| |
| def writeUuidsToFile(): Unit = { |
| if (captureUuids) { |
| val writer = { |
| val fos = new FileOutputStream(captureUuidFilename) |
| new PrintWriter(fos, false) |
| } |
| writer.println("name,uuid") |
| val uuidList: List[(Int, String)] = uuidMap.toList.sortBy(l => l._1) |
| uuidList.foreach { l => |
| writer.println(s"${Settings.entityPrefix}${l._1},${l._2}") |
| } |
| writer.flush() |
| writer.close() |
| } |
| } |
| |
| |
| val auditUuidsHeader = "collection,name,uuid,modified" |
| |
| case class AuditList(var collection: String, var entityName: String, var uuid: String, var modified: Long) |
| |
| // key: uuid, value: collection |
| private var auditEntityCounter: Long = 0 |
| private var lastAuditEntityCountPrinted: Long = 0 |
| private var auditUuidList: mutable.MutableList[AuditList] = mutable.MutableList[AuditList]() |
| def addAuditUuid(uuid: String, collection: String, entityName: String, modified: Long): Unit = { |
| if (captureAuditUuids) { |
| auditUuidList.synchronized { |
| auditUuidList += AuditList(collection, entityName, uuid, modified) |
| auditEntityCounter += 1 |
| if (logEntityProgress && (auditEntityCounter >= lastAuditEntityCountPrinted + entityProgressCount)) { |
| println(s"Entity: $auditEntityCounter") |
| lastAuditEntityCountPrinted = auditEntityCounter |
| } |
| } |
| } |
| } |
| |
| def writeAuditUuidsToFile(uuidDesc: String): Unit = { |
| if (captureAuditUuids) { |
| println(s"Sorting and writing ${auditUuidList.size} $uuidDesc UUIDs in CSV file $captureAuditUuidFilename") |
| val writer = { |
| val fos = new FileOutputStream(captureAuditUuidFilename) |
| new PrintWriter(fos, false) |
| } |
| writer.println(auditUuidsHeader) |
| val uuidList: List[AuditList] = auditUuidList.toList.sortBy(e => (e.collection, e.entityName, e.modified)) |
| uuidList.foreach { e => |
| writer.println(s"${e.collection},${e.entityName},${e.uuid},${e.modified}") |
| } |
| writer.flush() |
| writer.close() |
| } |
| } |
| |
| def getUserFeeder:Array[Map[String, String]]= { |
| FeederGenerator.generateUserWithGeolocationFeeder(totalUsers, userLocationRadius, centerLatitude, centerLongitude) |
| } |
| |
| def getInfiniteUserFeeder:Iterator[Map[String, String]]= { |
| FeederGenerator.generateUserWithGeolocationFeederInfinite(userSeed, userLocationRadius, centerLatitude, centerLongitude) |
| } |
| |
| private var testStartTime: Long = System.currentTimeMillis() |
| private var testEndTime: Long = 0 |
| |
| def getTestStartTime: Long = { |
| testStartTime |
| } |
| |
| def setTestStartTime(): Unit = { |
| testStartTime = System.currentTimeMillis() |
| } |
| |
| def setTestEndTime(): Unit = { |
| testEndTime = System.currentTimeMillis() |
| } |
| |
| def continueMinutesTest: Boolean = { |
| (System.currentTimeMillis() - testStartTime) < (endMinutes.toLong*60L*1000L) |
| } |
| |
| private val countAuditSuccess = new AtomicInteger(0) |
| private val countAuditNotFound = new AtomicInteger(0) |
| private val countAuditBadResponse = new AtomicInteger(0) |
| |
| def incAuditSuccess(): Unit = { |
| countAuditSuccess.incrementAndGet() |
| } |
| |
| def incAuditNotFound(): Unit = { |
| countAuditNotFound.incrementAndGet() |
| } |
| |
| def incAuditBadResponse(): Unit = { |
| countAuditBadResponse.incrementAndGet() |
| } |
| |
| def printAuditResults(): Unit = { |
| if (scenarioType == ScenarioType.AuditVerifyCollectionEntities) { |
| val countSuccess = countAuditSuccess.get |
| val countNotFound = countAuditNotFound.get |
| val countBadResponse = countAuditBadResponse.get |
| val countTotal = countSuccess + countNotFound + countBadResponse |
| |
| val seconds = ((testEndTime - testStartTime) / 1000).toInt |
| val s:Int = seconds % 60 |
| val m:Int = (seconds/60) % 60 |
| val h:Int = seconds/(60*60) |
| val elapsedStr = f"$h%d:$m%02d:$s%02d" |
| |
| println() |
| println("-----------------------------------------------------------------------------") |
| println("AUDIT RESULTS") |
| println("-----------------------------------------------------------------------------") |
| println() |
| println(s"Successful: $countSuccess") |
| println(s"Not Found: $countNotFound") |
| println(s"Bad Response: $countBadResponse") |
| println(s"Total: $countTotal") |
| println() |
| println(s"Start Timestamp(ms): $testStartTime") |
| println(s"End Timestamp(ms): $testEndTime") |
| println(s"Elapsed Time: $elapsedStr") |
| println() |
| println("-----------------------------------------------------------------------------") |
| println() |
| } |
| } |
| |
| def printSettingsSummary(afterTest: Boolean): Unit = { |
| val authTypeStr = authType + (if (authType == AuthType.Token) s"($tokenType)" else "") |
| val endConditionStr = if (endConditionType == EndConditionType.MinutesElapsed) s"$endMinutes minutes elapsed" else s"$endRequestCount requests" |
| val seconds = ((testEndTime - testStartTime) / 1000).toInt |
| val s:Int = seconds % 60 |
| val m:Int = (seconds/60) % 60 |
| val h:Int = seconds/(60*60) |
| val elapsedStr = f"$h%d:$m%02d:$s%02d" |
| println() |
| println("-----------------------------------------------------------------------------") |
| println("SIMULATION SETTINGS") |
| println("-----------------------------------------------------------------------------") |
| println() |
| println(s"ScenarioType:$scenarioType AuthType:$authTypeStr") |
| println() |
| println(s"BaseURL:$baseUrl") |
| println(s"Org:$org App:$app Collection:$collection") |
| println(s"CreateOrg:$createOrg CreateApp:$createApp LoadEntities:$loadEntities") |
| println(s"SandboxCollection:$sandboxCollection SkipSetup:$skipSetup") |
| println(s"AuthType:$authType TokenType:$tokenType AdminUser:$adminUser") |
| println() |
| println(s"EntityType:$entityType Prefix:$entityPrefix RetryCount:$retryCount") |
| if (scenarioType == ScenarioType.AuditGetCollectionEntities && laterThanTimestamp > 0) { |
| if (laterThanTimestamp > 0) println(s"SearchLimit:$searchLimit OnlyForEntriesAtOrLater:$laterThanTimestamp") |
| } else { |
| println(s"SearchLimit:$searchLimit SearchQuery:$searchQuery") |
| } |
| println() |
| println(s"Overall: NumEntities:$totalNumEntities Seed:$overallEntitySeed Workers:$entityWorkerCount") |
| println(s"Worker: NumEntities:$numEntities Seed:$entitySeed WorkerNum:$entityWorkerNum") |
| println() |
| println(s"Ramp: Users:$rampUsers Time:$rampTime") |
| println(s"Constant: UsersPerSec:$constantUsersPerSec Time:$constantUsersDuration") |
| println(s"EndCondition:$endConditionStr") |
| println() |
| if (feedUuids) println(s"Feed CSV: $feedUuidFilename") |
| if (feedAuditUuids) println(s"Audit Feed CSV: $feedAuditUuidFilename") |
| if (captureUuids) println(s"Capture CSV:$captureUuidFilename") |
| if (captureAuditUuids) { |
| if (scenarioType == ScenarioType.AuditVerifyCollectionEntities) |
| println(s"Audit Capture CSV (failed entities):$captureAuditUuidFilename") |
| else |
| println(s"Audit Capture CSV:$captureAuditUuidFilename") |
| } |
| println() |
| println() |
| if (afterTest) { |
| println(s"TestStarted:$testStartTime TestEnded:$testEndTime Elapsed: $elapsedStr") |
| } else { |
| println(s"TestStarted:$testStartTime") |
| } |
| println() |
| println("-----------------------------------------------------------------------------") |
| println() |
| } |
| |
| def mapFromQueryParamConfigString(queryParamConfigString: String): Map[String,String] = { |
| val params = mutable.Map[String,String]() |
| val paramStrings:Array[String] = queryParamConfigString split "&" |
| for (i <- paramStrings.indices) { |
| val param = paramStrings(i) |
| val pair = param split "=" |
| val key = URLDecoder.decode(pair(0), "UTF-8") |
| val value = pair.length match { |
| case l if l > 1 => URLDecoder.decode(pair(1), "UTF-8") |
| case _ => "" |
| } |
| params(key) = value |
| println(s"QueryParam $key = $value") |
| } |
| params.toMap |
| } |
| |
| printSettingsSummary(false) |
| |
| } |