├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── build.sbt ├── project ├── build.properties └── plugins.sbt ├── src ├── main │ └── scala │ │ └── com │ │ └── localytics │ │ └── sbt │ │ └── dynamodb │ │ ├── DeployDynamoDBLocal.scala │ │ ├── DynamoDBLocalKeys.scala │ │ ├── DynamoDBLocalPlugin.scala │ │ ├── PidUtils.scala │ │ ├── StartDynamoDBLocal.scala │ │ └── StopDynamoDBLocal.scala └── test │ ├── resources │ ├── invalid.jar │ ├── invalid.tar.gz │ ├── valid.jar │ └── valid.tar.gz │ └── scala │ └── com │ └── localytics │ └── sbt │ └── dynamodb │ ├── DeployDynamoDBLocalTest.scala │ └── PidUtilsTest.scala └── version.sbt /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.log 3 | 4 | # sbt specific 5 | .cache/ 6 | .history/ 7 | .lib/ 8 | dist/* 9 | target/ 10 | lib_managed/ 11 | src_managed/ 12 | project/boot/ 13 | project/plugins/project/ 14 | 15 | # Scala-IDE specific 16 | .scala_dependencies 17 | .worksheet 18 | .idea 19 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | This project adheres to [Semantic Versioning](http://semver.org/). 4 | 5 | ## [2.0.3] - 2018-06-18 6 | - Upgrade sbt from 1.1.4 to 1.1.6 7 | Thanks to [sullis](https://github.com/sullis) for the PR! 8 | 9 | ## [2.0.2] - 2018-04-24 10 | - Upgrade Scalatest from 3.0.1 to 3.0.5 11 | - Upgrade sbt from 1.0.0 to 1.1.4 12 | - Upgrade sbt-release from 1.0.6 to 1.0.8 13 | - Thanks to [sullis](https://github.com/sullis) for the PRs! 14 | 15 | ## [2.0.1] - 2017-11-22 16 | - Fix TestOnly instruction in README 17 | 18 | ## [2.0.0] - 2017-09-19 19 | - Upgrade to SBT 1.0/Scala 2.12 20 | - Thanks to [Jeff Wilde](https://github.com/jeffwilde) for the PR! 21 | 22 | ## [1.5.5] - 2017-03-08 23 | - Fix Windows bugs caused by Windows paths 24 | 25 | ## [1.5.4] - 2017-02-27 26 | - Enable different settings for different configurations 27 | 28 | ## [1.5.3] - 2017-02-02 29 | - Update to new URL scheme for DynamoDB Local jar 30 | 31 | ## [1.5.2] - 2017-01-19 32 | - Fix PidUtil class collision when using multiple localytics/sbt-* projects 33 | 34 | ## [1.5.1] - 2017-01-17 35 | - Fix #26 - bug in shutdown 36 | 37 | ## [1.5.0] - 2016-12-13 38 | - Refactor to best practices and SBT 1.0 syntax 39 | 40 | ## [1.4.3] - 2016-11-10 41 | - Use new syntax to remove warnings in sbt 0.13.13. Thanks @philwills. 42 | 43 | ## [1.4.2] - 2016-08-15 44 | - Skip unnecessary untargz 45 | - Verify gz and jar files during deploy stage 46 | 47 | ## [1.4.1] - 2016-05-03 48 | - Ensure we kill the right DynamoDB process 49 | 50 | ## [1.4.0] - 2016-03-09 51 | - Refactor cleanup after tests to work with `testOnly` 52 | 53 | ## [1.3.1] - 2016-03-09 54 | - Fail if the tar.gz file is corrupt 55 | 56 | ## [1.3.0] - 2016-02-22 57 | - Add HeapSize configuration 58 | 59 | ## [1.2.1] - 2016-01-26 60 | - Fix broken task names 61 | 62 | ## [1.2] - 2016-01-12 63 | - Make lifecycle management explict and easy to use outside of the default test config 64 | - Make use of autoplugin to automatically import keys and settings 65 | - Converted packages to com.localtyics 66 | - Support test configuration where DynamoDB was never started ([grahamar/sbt-dynamodb#8](https://github.com/grahamar/sbt-dynamodb/pull/8)) 67 | - adds -sharedDB flag ([grahamar/sbt-dynamodb#7](https://github.com/grahamar/sbt-dynamodb/pull/7)) 68 | - Do not download the "latest" jar on every test run ([grahamar/sbt-dynamodb#4](https://github.com/grahamar/sbt-dynamodb/pull/4)) 69 | 70 | ## [1.1] - 2015-03-10 71 | - Improved Cygwin/Windows support 72 | - Killing PID with 'Taskkill' in Windows environment 73 | - Downloading DynamoDB from S3 only when file does not exist locally or version set to latest 74 | - Additional log output 75 | 76 | ## [1.0] - 2015-01-20 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Fork, then clone the repo: 4 | 5 | git clone git@github.com:your-username/sbt-dynamodb.git 6 | 7 | Create a new branch for your work: 8 | 9 | git checkout -b 10 | 11 | Code and document your new feature: 12 | 13 | We appreciate well organized commits, commit messages 14 | 15 | Push your changes up to GitHub: 16 | 17 | git push -u origin 18 | 19 | Open a pull request: 20 | 21 | We appreciate well organized pull request messages 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Localytics 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | sbt-dynamodb 2 | =============== 3 | 4 | Support for running [DynamoDB Local](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html) in tests. 5 | 6 | [![MIT license](https://img.shields.io/badge/license-MIT%20License-blue.svg)](LICENSE) 7 | [ ![Download](https://api.bintray.com/packages/localytics/sbt-plugins/sbt-dynamodb/images/download.svg) ](https://bintray.com/localytics/sbt-plugins/sbt-dynamodb/_latestVersion) 8 | 9 | Installation 10 | ------------ 11 | Add the following to your `project/plugins.sbt` file: 12 | 13 | For sbt 1.0: 14 | ``` 15 | addSbtPlugin("com.localytics" % "sbt-dynamodb" % "2.0.2") 16 | ``` 17 | 18 | For sbt 0.13.5+ 19 | 20 | ``` 21 | addSbtPlugin("com.localytics" % "sbt-dynamodb" % "1.5.5") 22 | ``` 23 | 24 | sbt 0.13.6+ is supported, 0.13.5 should work with the right bintray resolvers 25 | 26 | Usage 27 | ----- 28 | 29 | To use DynamoDB Local in your project you can call `start-dynamodb-local` and `stop-dynamodb-local` directly in `sbt`. 30 | 31 | Configuration 32 | ------------- 33 | 34 | To have DynamoDB Local automatically start and stop around your tests 35 | 36 | ``` 37 | startDynamoDBLocal := startDynamoDBLocal.dependsOn(compile in Test).value 38 | test in Test := (test in Test).dependsOn(startDynamoDBLocal).value 39 | testOnly in Test := (testOnly in Test).dependsOn(startDynamoDBLocal).evaluated 40 | testOptions in Test += dynamoDBLocalTestCleanup.value 41 | ``` 42 | 43 | To download the DynamoDB Local jar to a specific location ("dynamodb-local" is the default) 44 | 45 | ``` 46 | dynamoDBLocalDownloadDir := file("my-dir") 47 | ``` 48 | 49 | To use a specific version ("latest" is the default DynamoDB version to download and run) 50 | 51 | ``` 52 | dynamoDBLocalVersion := "2014-10-07" 53 | ``` 54 | 55 | If the "latest" version is being used, specify how old the downloaded copy should be before attempting a new download (default is 2 days) 56 | 57 | ``` 58 | import scala.concurrent.duration._ 59 | dynamoDBLocalDownloadIfOlderThan := 2.days 60 | ``` 61 | 62 | To specify a port other than the default `8000` 63 | 64 | ``` 65 | dynamoDBLocalPort := 8080 66 | ``` 67 | 68 | To override the default JVM heap size (specified in MB) 69 | 70 | ``` 71 | dynamoDBLocalHeapSize := Some(1024) 72 | ``` 73 | 74 | The default for the DynamoDB Local instance is to run in "in-memory" mode. To use a persistent file based mode you need to set both the data path & turn off in-memory. The plugin will fail to overwrite an existing db file at this path. 75 | 76 | ``` 77 | dynamoDBLocalInMemory := false 78 | dynamoDBLocalDBPath := Some("some/directory/here") 79 | ``` 80 | 81 | The default for DynamoDB Local instance is to use a separate file for each credential and region. To allow all all DynamoDB clients to interact with the same set of tables regardless of their region and credentials enable "shared db" mode. 82 | 83 | ``` 84 | dynamoDBLocalSharedDB := true 85 | ``` 86 | 87 | The default on stop is to cleanup any data directory if specified. This can be changed using 88 | 89 | ``` 90 | dynamoDBLocalCleanAfterStop := false 91 | ``` 92 | 93 | Debugging db Contents 94 | ------ 95 | 96 | Configure your app to persist to disk: 97 | 98 | ``` 99 | dynamoDBLocalInMemory := false 100 | dynamoDBLocalDBPath := Some("some/directory/here") 101 | ``` 102 | 103 | Once you have completed some operation involving dynamo, you can inspect the contents with sqlite3. Example: 104 | 105 | ``` 106 | $ sqlite3 /tmp/dynamo/test/AXAVAXAJUNK_us-east-1.db "select ObjectJSON from \"table\"" | jq '.device_id,.env' 107 | { 108 | "N":"123456789" 109 | }, 110 | { 111 | "S":"Production" 112 | } 113 | ``` 114 | 115 | Scopes 116 | ------ 117 | 118 | By default this plugin lives entirely in the `Global` scope. However, different settings for different scopes is possible. For instance, you can add the plugin to the `Test` scope using 119 | 120 | ``` 121 | inConfig(Test)(baseDynamoDBSettings) 122 | ``` 123 | 124 | You can then adjust the settings within the `Test` scope using 125 | 126 | ``` 127 | (dynamoDBLocalDownloadDir in Test) := file("in-test/dynamo-db") 128 | ``` 129 | 130 | and you can execute the plugin tasks within the `Test` scope using 131 | 132 | ``` 133 | sbt test:start-dynamodb-local 134 | ``` 135 | 136 | Similarly, you can have the plugin automatically start and stop around your tests using 137 | 138 | ``` 139 | startDynamoDBLocal in Test := (startDynamoDBLocal in Test).dependsOn(compile in Test).value 140 | test in Test := (test in Test).dependsOn(startDynamoDBLocal in Test).value 141 | testOnly in Test := (testOnly in Test).dependsOn(startDynamoDBLocal in Test).evaluated 142 | testOptions in Test += (dynamoDBLocalTestCleanup in Test).value 143 | ``` 144 | 145 | Thanks 146 | ----- 147 | 148 | This work is based on the [Maven Plugin for DynamoDB](https://github.com/jcabi/jcabi-dynamodb-maven-plugin). 149 | 150 | The [initial implementation](https://github.com/grahamar/sbt-dynamodb) was developed by [Graham Rhodes](https://github.com/grahamar). 151 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | name := "sbt-dynamodb" 2 | 3 | description := "Support for running DynamoDB Local in your integration tests" 4 | 5 | organization := "com.localytics" 6 | 7 | scalaVersion := "2.12.3" 8 | 9 | // Sane set of compiler flags 10 | scalacOptions ++= Seq( 11 | "-deprecation", // Emit warning and location for usages of deprecated APIs 12 | "-encoding", "UTF-8", // Specify character encoding used by source files (yes, this is 2 args) 13 | "-feature", // Emit warning and location for usages of features that should be imported explicitly 14 | "-unchecked", // Enable detailed unchecked (erasure) warnings 15 | "-Xfatal-warnings", // Fail the compilation if there are any warnings 16 | "-Xlint", // Enable recommended additional warnings 17 | "-Xfuture", // Turn on future language features. 18 | "-Yno-adapted-args" // Do not adapt an argument list (by inserting () or creating a tuple) to match the receiver 19 | ) 20 | 21 | // This is an auto plugin 22 | // http://www.scala-sbt.org/1.x/docs/Plugins.html#Creating+an+auto+plugin 23 | sbtPlugin := true 24 | 25 | // Generate a POM 26 | // http://www.scala-sbt.org/1.x/docs/Publishing.html#Modifying+the+generated+POM 27 | publishMavenStyle := false 28 | 29 | // MIT License for bintray 30 | // https://github.com/softprops/bintray-sbt#licenses 31 | licenses += ("MIT", url("http://opensource.org/licenses/MIT")) 32 | 33 | // Publish to the Localytics organization 34 | // https://github.com/softprops/bintray-sbt#publishing 35 | bintrayOrganization := Some("localytics") 36 | 37 | // Bintray labels 38 | // https://github.com/softprops/bintray-sbt#labels 39 | bintrayPackageLabels := Seq("localytics", "sbt", "aws", "dynamodb", "test", "testing") 40 | 41 | // Error on conflicting dependencies 42 | // http://www.scala-sbt.org/1.x/docs/Library-Management.html#Conflict+Management 43 | conflictManager := ConflictManager.strict 44 | 45 | // Error on circular dependencies 46 | // http://www.scala-sbt.org/1.x/docs/sbt-0.13-Tech-Previews.html#Circular+dependency 47 | updateOptions := updateOptions.value.withCircularDependencyLevel(CircularDependencyLevel.Error) 48 | 49 | libraryDependencies += 50 | ( "org.scalatest" %% "scalatest" % "3.0.5" % "test" 51 | // Scalatest 3.0.5 is slightly behind sbt and Scala dependencies for these two modules, so they must be excluded 52 | // while Strict dependency checking is enabled. 53 | exclude("org.scala-lang.modules", s"scala-xml_${scalaBinaryVersion.value}") 54 | exclude("org.scala-lang.modules", s"scala-parser-combinators_${scalaBinaryVersion.value}") ) 55 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.1.6 2 | 3 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("org.foundweekends" % "sbt-bintray" % "0.5.1") 2 | 3 | addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.8") 4 | -------------------------------------------------------------------------------- /src/main/scala/com/localytics/sbt/dynamodb/DeployDynamoDBLocal.scala: -------------------------------------------------------------------------------- 1 | package com.localytics.sbt.dynamodb 2 | 3 | import java.io.FileInputStream 4 | import java.net.URL 5 | import java.util.zip.GZIPInputStream 6 | import java.util.zip.ZipFile 7 | 8 | import sbt.File 9 | import sbt.Keys._ 10 | 11 | import scala.concurrent.duration.Duration 12 | import scala.sys.process._ 13 | import scala.util.Try 14 | 15 | object DeployDynamoDBLocal { 16 | 17 | private[dynamodb] def validJar(file: File): Boolean = Try(new ZipFile(file)).isSuccess 18 | 19 | private[dynamodb] def validGzip(file: File): Boolean = Try(new GZIPInputStream(new FileInputStream(file)).read()).isSuccess 20 | 21 | def apply(ver: String, url: Option[String], targetDir: File, downloadIfOlderThan: Duration, streamz: TaskStreams): File = { 22 | val targz = new File(targetDir, s"dynamodb_local_$ver.tar.gz") 23 | val jar = new File(targetDir, "DynamoDBLocal.jar") 24 | 25 | def isStale(file: File) = ver == "latest" && System.currentTimeMillis - file.lastModified() > downloadIfOlderThan.toMillis 26 | 27 | if (!targetDir.exists()) { 28 | streamz.log.info(s"Creating DynamoDB Local directory $targetDir") 29 | targetDir.mkdirs() 30 | } 31 | if (!targz.exists() || isStale(targz) || !validGzip(targz)) { 32 | val remoteFile = url.getOrElse(s"https://s3-us-west-2.amazonaws.com/dynamodb-local/dynamodb_local_$ver.tar.gz") 33 | streamz.log.info(s"Downloading targz from [$remoteFile] to [${targz.getAbsolutePath}]") 34 | (new URL(remoteFile) #> targz).!! 35 | } 36 | if (!validGzip(targz)) sys.error(s"Invalid gzip file at [${targz.getAbsolutePath}]") 37 | if (!jar.exists() || !validJar(jar)) { 38 | streamz.log.info(s"Extracting jar from [${targz.getAbsolutePath}] to [${jar.getAbsolutePath}]") 39 | Process(Seq("tar", "xzf", targz.getName), targetDir).!! 40 | } 41 | if (!validJar(jar)) sys.error(s"Invalid jar file at [${jar.getAbsolutePath}]") 42 | jar 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/scala/com/localytics/sbt/dynamodb/DynamoDBLocalKeys.scala: -------------------------------------------------------------------------------- 1 | package com.localytics.sbt.dynamodb 2 | 3 | import java.io.File 4 | 5 | import sbt.Keys._ 6 | import sbt._ 7 | 8 | import scala.concurrent.duration._ 9 | 10 | object DynamoDBLocalKeys { 11 | lazy val dynamoDBLocalVersion = settingKey[String]("DynamoDB Local version to download. Defaults to latest.") 12 | lazy val dynamoDBLocalDownloadUrl = settingKey[Option[String]]("DynamoDB Local URL to download jar from (optional).") 13 | lazy val dynamoDBLocalDownloadDir = settingKey[File]("The directory the DynamoDB Local jar will be downloaded to. Defaults to dynamodb-local.") 14 | lazy val dynamoDBLocalDownloadIfOlderThan = settingKey[Duration]("Re-download the jar if the existing one is older than this. Defaults to 2 days.") 15 | lazy val dynamoDBLocalHeapSize = settingKey[Option[Int]]("The size of the heap for DynamoDB Local. Defaults to the JVM default.") 16 | lazy val dynamoDBLocalPort = settingKey[Int]("The port number that DynamoDB Local will use to communicate with your application. Defaults to 8000.") 17 | lazy val dynamoDBLocalDBPath = settingKey[Option[String]]("The directory where DynamoDB Local will write its database file. Defaults to the current directory.") 18 | lazy val dynamoDBLocalInMemory = settingKey[Boolean]("Instead of using a database file, DynamoDB Local will run in memory. When you stop DynamoDB Local, none of the data will be saved.") 19 | lazy val dynamoDBLocalSharedDB = settingKey[Boolean]("DynamoDB Local will use a single, shared database file. All clients will interact with the same set of tables regardless of their region and credential configuration.") 20 | lazy val dynamoDBLocalCleanAfterStop = settingKey[Boolean]("Clean the local data directory after DynamoDB Local shutdown. Defaults to true.") 21 | 22 | lazy val deployDynamoDBLocal = TaskKey[File]("deploy-dynamodb-local") 23 | lazy val startDynamoDBLocal = TaskKey[String]("start-dynamodb-local") 24 | lazy val stopDynamoDBLocal = TaskKey[Unit]("stop-dynamodb-local") 25 | lazy val dynamoDBLocalTestCleanup = TaskKey[Tests.Cleanup]("dynamodb-local-test-cleanup") 26 | 27 | // http://www.scala-sbt.org/0.13/docs/Plugins-Best-Practices.html#Provide+raw+settings+and+configured+settings 28 | lazy val baseDynamoDBSettings = Seq( 29 | dynamoDBLocalVersion := "latest", 30 | dynamoDBLocalDownloadUrl := None, 31 | dynamoDBLocalDownloadDir := file("dynamodb-local"), 32 | dynamoDBLocalDownloadIfOlderThan := 2.days, 33 | dynamoDBLocalPort := 8000, 34 | dynamoDBLocalDBPath := None, 35 | dynamoDBLocalHeapSize := None, 36 | dynamoDBLocalInMemory := true, 37 | dynamoDBLocalSharedDB := false, 38 | dynamoDBLocalCleanAfterStop := true, 39 | deployDynamoDBLocal := DeployDynamoDBLocal( 40 | dynamoDBLocalVersion.value, 41 | dynamoDBLocalDownloadUrl.value, 42 | dynamoDBLocalDownloadDir.value, 43 | dynamoDBLocalDownloadIfOlderThan.value, 44 | streams.value), 45 | startDynamoDBLocal := StartDynamoDBLocal( 46 | dynamoDBLocalDownloadDir.value, 47 | dynamoDBLocalPort.value, 48 | dynamoDBLocalHeapSize.value, 49 | dynamoDBLocalDBPath.value, 50 | dynamoDBLocalInMemory.value, 51 | dynamoDBLocalSharedDB.value, 52 | streams.value), 53 | stopDynamoDBLocal := StopDynamoDBLocal( 54 | dynamoDBLocalDBPath.value, 55 | dynamoDBLocalCleanAfterStop.value, 56 | dynamoDBLocalPort.value, 57 | dynamoDBLocalDownloadDir.value, 58 | streams.value), 59 | dynamoDBLocalTestCleanup := { 60 | val dynamoDBLocalDBPathValue = dynamoDBLocalDBPath.value 61 | val dynamoDBLocalCleanAfterStopValue = dynamoDBLocalCleanAfterStop.value 62 | val dynamoDBLocalPortValue = dynamoDBLocalPort.value 63 | val dynamoDBLocalDownloadDirValue = dynamoDBLocalDownloadDir.value 64 | val streamsValue = streams.value 65 | Tests.Cleanup(() => 66 | StopDynamoDBLocal( 67 | dynamoDBLocalDBPathValue, 68 | dynamoDBLocalCleanAfterStopValue, 69 | dynamoDBLocalPortValue, 70 | dynamoDBLocalDownloadDirValue, 71 | streamsValue)) 72 | }, 73 | startDynamoDBLocal := startDynamoDBLocal 74 | .dependsOn(deployDynamoDBLocal) 75 | .value 76 | ) 77 | } 78 | -------------------------------------------------------------------------------- /src/main/scala/com/localytics/sbt/dynamodb/DynamoDBLocalPlugin.scala: -------------------------------------------------------------------------------- 1 | package com.localytics.sbt.dynamodb 2 | 3 | import sbt._ 4 | 5 | object DynamoDBLocalPlugin extends AutoPlugin { 6 | 7 | // auto enable plugin http://www.scala-sbt.org/0.13/docs/Plugins.html#Root+plugins+and+triggered+plugins 8 | override val trigger = allRequirements 9 | 10 | // inject project keys http://www.scala-sbt.org/0.13/docs/Plugins.html#Controlling+the+import+with+autoImport 11 | val autoImport = DynamoDBLocalKeys 12 | 13 | // inject project settings http://www.scala-sbt.org/0.13/docs/Plugins.html#projectSettings+and+buildSettings 14 | override lazy val projectSettings = DynamoDBLocalKeys.baseDynamoDBSettings 15 | } 16 | -------------------------------------------------------------------------------- /src/main/scala/com/localytics/sbt/dynamodb/PidUtils.scala: -------------------------------------------------------------------------------- 1 | package com.localytics.sbt.dynamodb 2 | 3 | import java.io.File 4 | import java.util.regex.Pattern 5 | 6 | object PidUtils { 7 | 8 | def extractPid(input: String, port: Int, jar: File): Option[String] = { 9 | val pidPortRegex = s"\\d+ ${Pattern.quote(jar.getAbsolutePath)} -port $port".r 10 | pidPortRegex.findFirstIn(input).map(_.split(" ")(0)) 11 | } 12 | 13 | def osName: String = System.getProperty("os.name") match { 14 | case n: String if !n.isEmpty => n 15 | case _ => System.getProperty("os") 16 | } 17 | 18 | def killPidCommand(pid: String): String = 19 | if (osName.toLowerCase.contains("windows")) s"Taskkill /PID $pid /F" else s"kill $pid" 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/scala/com/localytics/sbt/dynamodb/StartDynamoDBLocal.scala: -------------------------------------------------------------------------------- 1 | package com.localytics.sbt.dynamodb 2 | 3 | import java.net.Socket 4 | 5 | import sbt.File 6 | import sbt.Keys._ 7 | 8 | import scala.sys.process._ 9 | import scala.util.Try 10 | 11 | object StartDynamoDBLocal { 12 | 13 | private[dynamodb] def isDynamoDBLocalRunning(port: Int): Boolean = Try(new Socket("localhost", port).close()).isSuccess 14 | 15 | def apply(baseDir: File, port: Int, heapSize: Option[Int], dbPath: Option[String], inMem: Boolean, shared: Boolean, streamz: TaskStreams): String = { 16 | val jar = new File(baseDir, "DynamoDBLocal.jar") 17 | val lib = new File(baseDir, "DynamoDBLocal_lib") 18 | 19 | val args = Seq("java", s"-Djava.library.path=${lib.getAbsolutePath}") ++ 20 | heapSize.map(mb => Seq(s"-Xms${mb}m", s"-Xmx${mb}m")).getOrElse(Nil) ++ 21 | Seq("-jar", jar.getAbsolutePath) ++ 22 | Seq("-port", port.toString) ++ 23 | dbPath.map(db => Seq("-dbPath", db)).getOrElse(Nil) ++ 24 | (if (inMem) Seq("-inMemory") else Nil) ++ 25 | (if (shared) Seq("-sharedDb") else Nil) 26 | 27 | if (isDynamoDBLocalRunning(port)) { 28 | streamz.log.warn(s"dynamodb local is already running on port $port") 29 | } else { 30 | streamz.log.info("Starting dynamodb local") 31 | Process(args).run() 32 | do { 33 | streamz.log.info(s"Waiting for dynamodb local to boot on port $port") 34 | Thread.sleep(500) 35 | } while (!isDynamoDBLocalRunning(port)) 36 | } 37 | PidUtils.extractPid("jps -ml".!!, port, jar).getOrElse { 38 | sys.error(s"Cannot find dynamodb local PID running on $port") 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/scala/com/localytics/sbt/dynamodb/StopDynamoDBLocal.scala: -------------------------------------------------------------------------------- 1 | package com.localytics.sbt.dynamodb 2 | 3 | import sbt.File 4 | import sbt.Keys._ 5 | 6 | import scala.sys.process._ 7 | 8 | object StopDynamoDBLocal { 9 | 10 | def apply(dbPathOpt: Option[String], clean: Boolean, port: Int, baseDir: File, streamz: TaskStreams): Unit = { 11 | val jar = new File(baseDir, "DynamoDBLocal.jar") 12 | PidUtils.extractPid("jps -ml".!!, port, jar) match { 13 | case Some(pid) => 14 | streamz.log.info("Stopping dynamodb local") 15 | PidUtils.killPidCommand(pid).! 16 | case None => 17 | streamz.log.warn("Cannot find dynamodb local PID") 18 | } 19 | if (clean) dbPathOpt.foreach { dbPath => 20 | streamz.log.info("Cleaning dynamodb local") 21 | val dir = new File(dbPath) 22 | if (dir.exists()) sbt.IO.delete(dir) 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/test/resources/invalid.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/localytics/sbt-dynamodb/b90599481b0b86c85235a73144de507527e0537c/src/test/resources/invalid.jar -------------------------------------------------------------------------------- /src/test/resources/invalid.tar.gz: -------------------------------------------------------------------------------- 1 | ��8�W�ҿk�@�q��/N 2 | %\~� �:�;8�W�H�L��"H��š�nR��gy8���^�emL��>��#(��<�K��bL�^ϣ��$I�E�g*- 3 | -------------------------------------------------------------------------------- /src/test/resources/valid.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/localytics/sbt-dynamodb/b90599481b0b86c85235a73144de507527e0537c/src/test/resources/valid.jar -------------------------------------------------------------------------------- /src/test/resources/valid.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/localytics/sbt-dynamodb/b90599481b0b86c85235a73144de507527e0537c/src/test/resources/valid.tar.gz -------------------------------------------------------------------------------- /src/test/scala/com/localytics/sbt/dynamodb/DeployDynamoDBLocalTest.scala: -------------------------------------------------------------------------------- 1 | package com.localytics.sbt.dynamodb 2 | 3 | import java.io.File 4 | 5 | import org.scalatest.FunSpec 6 | import org.scalatest.Matchers 7 | 8 | class DeployDynamoDBLocalTest extends FunSpec with Matchers { 9 | 10 | describe("DeployDynamoDBLocal") { 11 | 12 | it("should identify a valid jar") { 13 | DeployDynamoDBLocal.validJar(new File(getClass.getResource("/valid.jar").getFile)) should be(true) 14 | } 15 | 16 | it("should identify an invalid jar") { 17 | DeployDynamoDBLocal.validJar(new File(getClass.getResource("/invalid.jar").getFile)) should be(false) 18 | } 19 | 20 | it("should identify a valid gz") { 21 | DeployDynamoDBLocal.validGzip(new File(getClass.getResource("/valid.tar.gz").getFile)) should be(true) 22 | } 23 | 24 | it("should identify an invalid gz") { 25 | DeployDynamoDBLocal.validGzip(new File(getClass.getResource("/invalid.tar.gz").getFile)) should be(false) 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/test/scala/com/localytics/sbt/dynamodb/PidUtilsTest.scala: -------------------------------------------------------------------------------- 1 | package com.localytics.sbt.dynamodb 2 | 3 | import java.io.File 4 | 5 | import org.scalatest.FunSpec 6 | import org.scalatest.Matchers 7 | 8 | class PidUtilsTest extends FunSpec with Matchers { 9 | 10 | describe("PidUtils") { 11 | val jarPath = if (PidUtils.osName.toLowerCase.contains("windows")) 12 | "C:\\Users\\person\\code\\repository\\aws-mocks\\.dynamo-db\\DynamoDBLocal.jar" 13 | else 14 | "/Users/person/code/repository/aws-mocks/.dynamo-db/DynamoDBLocal.jar" 15 | 16 | it("should extract PID correctly") { 17 | val jpsOutput = 18 | s""" 19 | |92433 sun.tools.jps.Jps -ml 20 | |88818 /usr/local/Cellar/sbt/0.13.7/libexec/sbt-launch.jar 21 | |88421 /usr/local/Cellar/sbt/0.13.7/libexec/sbt-launch.jar 22 | |5895 com.intellij.database.remote.RemoteJdbcServer com.mysql.jdbc.Driver 23 | |92431 $jarPath -port 8000 -inMemory -sharedDb 24 | |74414 /usr/local/Cellar/sbt/0.13.7/libexec/sbt-launch.jar 25 | """.stripMargin 26 | val jar = new File(jarPath) 27 | PidUtils.extractPid(jpsOutput, 8000, jar) should equal(Some("92431")) 28 | } 29 | 30 | it("should resolve multiple local dynamodb instances") { 31 | val jpsOutput = 32 | s""" 33 | |92433 sun.tools.jps.Jps -ml 34 | |92430 $jarPath -port 8001 -inMemory -sharedDb 35 | |92433 $jarPath -port 8002 -inMemory -sharedDb 36 | |5895 com.intellij.database.remote.RemoteJdbcServer com.mysql.jdbc.Driver 37 | |92431 $jarPath -port 8000 -inMemory -sharedDb 38 | |74414 /usr/local/Cellar/sbt/0.13.7/libexec/sbt-launch.jar 39 | """.stripMargin 40 | val jar = new File(jarPath) 41 | PidUtils.extractPid(jpsOutput, 8000, jar) should equal(Some("92431")) 42 | 43 | } 44 | 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /version.sbt: -------------------------------------------------------------------------------- 1 | version in ThisBuild := "2.0.4-SNAPSHOT" 2 | --------------------------------------------------------------------------------