From 91d0ce38af8b5f9fd73358c4f2e9b414c77f7466 Mon Sep 17 00:00:00 2001 From: alesavin Date: Sun, 21 Apr 2019 23:46:51 +0200 Subject: [PATCH 1/2] add zookeeper impl, resolve #4 --- build.sbt | 16 +-- .../alesavin/cockpit/impl/HoldersDesk.scala | 2 +- .../alesavin/cockpit/impl/InMemoryDesk.scala | 4 +- .../ru/alesavin/cockpit/model/Control.scala | 2 + .../alesavin/cockpit/impl/DeskSpecBase.scala | 2 +- .../ru/alesavin/cockpit/impl/Storage.scala | 16 +++ .../alesavin/cockpit/impl/ZookeeperDesk.scala | 68 ++++++++++++ .../cockpit/impl/ZookeeperStorage.scala | 61 +++++++++++ .../cockpit/impl/ZookeeperDeskSpec.scala | 36 +++++++ .../cockpit/impl/ZookeeperStorageSpec.scala | 100 ++++++++++++++++++ 10 files changed, 297 insertions(+), 10 deletions(-) create mode 100644 zookeeper/src/main/scala/ru/alesavin/cockpit/impl/Storage.scala create mode 100644 zookeeper/src/main/scala/ru/alesavin/cockpit/impl/ZookeeperDesk.scala create mode 100644 zookeeper/src/main/scala/ru/alesavin/cockpit/impl/ZookeeperStorage.scala create mode 100644 zookeeper/src/test/scala/ru/alesavin/cockpit/impl/ZookeeperDeskSpec.scala create mode 100644 zookeeper/src/test/scala/ru/alesavin/cockpit/impl/ZookeeperStorageSpec.scala diff --git a/build.sbt b/build.sbt index c6c10b0..7414cd8 100644 --- a/build.sbt +++ b/build.sbt @@ -11,6 +11,10 @@ crossScalaVersions := Seq("2.12.7") conflictManager := ConflictManager.strict +val JunitVersion = "4.12" +val ScalaTestVersion = "3.0.5" +val CuratorVersion = "4.2.0" + val customScalacOptions = Seq( "-unchecked", "-deprecation", @@ -23,8 +27,8 @@ val customScalacOptions = Seq( ) val customDependencies = Seq( - "junit" % "junit" % "4.12" % Test, - "org.scalatest" %% "scalatest" % "3.0.5" % Test + "junit" % "junit" % JunitVersion % Test, + "org.scalatest" %% "scalatest" % ScalaTestVersion % Test ) lazy val root = (project in file(".")) @@ -43,9 +47,10 @@ lazy val zookeeper = (project in file("zookeeper")) name := "cockpit-zookeeper", scalacOptions ++= customScalacOptions, libraryDependencies ++= customDependencies ++ Seq( - "org.apache.curator" % "curator-framework" % "4.2.0" + "org.apache.curator" % "curator-framework" % CuratorVersion, + "org.apache.curator" % "curator-test" % CuratorVersion % Test, ) - ).dependsOn(core % "compile->compile,test->test") + ).dependsOn(core % "compile->compile;test->test") lazy val akkaHttp = (project in file("akka-http")) .settings( @@ -54,6 +59,5 @@ lazy val akkaHttp = (project in file("akka-http")) libraryDependencies ++= customDependencies ) -//coverageEnabled := true - +// TODO coverageEnabled := true // TODO scalastyle \ No newline at end of file diff --git a/core/src/main/scala/ru/alesavin/cockpit/impl/HoldersDesk.scala b/core/src/main/scala/ru/alesavin/cockpit/impl/HoldersDesk.scala index f275bc1..f70caf7 100644 --- a/core/src/main/scala/ru/alesavin/cockpit/impl/HoldersDesk.scala +++ b/core/src/main/scala/ru/alesavin/cockpit/impl/HoldersDesk.scala @@ -44,7 +44,7 @@ abstract class HoldersDesk[F[_]](ft: ControlTypes, init: String): Control[V] protected def updateInner(name: String) - (current2next: String => String): F[Unit] + (updater: String => String): F[Unit] } object HoldersDesk { diff --git a/core/src/main/scala/ru/alesavin/cockpit/impl/InMemoryDesk.scala b/core/src/main/scala/ru/alesavin/cockpit/impl/InMemoryDesk.scala index ccdc806..bf394b0 100644 --- a/core/src/main/scala/ru/alesavin/cockpit/impl/InMemoryDesk.scala +++ b/core/src/main/scala/ru/alesavin/cockpit/impl/InMemoryDesk.scala @@ -44,10 +44,10 @@ class InMemoryDesk(ft: ControlTypes, } override protected def updateInner(name: String) - (current2next: String => String): Future[Unit] = + (updater: String => String): Future[Unit] = synchronized { Future.fromTry(Try { val old = featureMap(name) - val n = current2next(old) + val n = updater(old) featureMap.update(name, n) })} } diff --git a/core/src/main/scala/ru/alesavin/cockpit/model/Control.scala b/core/src/main/scala/ru/alesavin/cockpit/model/Control.scala index 716cde4..077a90a 100644 --- a/core/src/main/scala/ru/alesavin/cockpit/model/Control.scala +++ b/core/src/main/scala/ru/alesavin/cockpit/model/Control.scala @@ -18,6 +18,8 @@ trait Control[V] { override def hashCode: Int = name.hashCode + + override def toString: String = s"Control($name)" } object Control { diff --git a/core/src/test/scala/ru/alesavin/cockpit/impl/DeskSpecBase.scala b/core/src/test/scala/ru/alesavin/cockpit/impl/DeskSpecBase.scala index 9b4bfa4..c46d1a5 100644 --- a/core/src/test/scala/ru/alesavin/cockpit/impl/DeskSpecBase.scala +++ b/core/src/test/scala/ru/alesavin/cockpit/impl/DeskSpecBase.scala @@ -102,7 +102,7 @@ trait DeskSpecBase d.register(name, 122) d.delete(name).futureValue shouldBe true - d.list.futureValue.isEmpty shouldBe true + d.list.futureValue shouldBe empty } "return false on calling delete for nonexistent feature" in { val d = desk() diff --git a/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/Storage.scala b/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/Storage.scala new file mode 100644 index 0000000..6b45e33 --- /dev/null +++ b/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/Storage.scala @@ -0,0 +1,16 @@ +package ru.alesavin.cockpit.impl + +import scala.util.Try + +/** + * TODO + * + * @author alesavin + */ +trait Storage { + + def keys: Try[Iterable[String]] // TODO Curator Async => Future + def get(key: String): Try[Option[String]] + def set(key: String, value: String): Try[Unit] + def remove(key: String): Try[Boolean] +} diff --git a/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/ZookeeperDesk.scala b/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/ZookeeperDesk.scala new file mode 100644 index 0000000..85531aa --- /dev/null +++ b/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/ZookeeperDesk.scala @@ -0,0 +1,68 @@ +package ru.alesavin.cockpit.impl + +import java.util.NoSuchElementException + +import ru.alesavin.cockpit.impl.HoldersDesk.HolderControlType +import ru.alesavin.cockpit.impl.ZookeeperDesk.registrationLock +import ru.alesavin.cockpit.model.{Control, ControlType, ControlTypes, Holder} + +import scala.concurrent.Future +import scala.util.{Failure, Success, Try} + +/** + * TODO + * + * @author alesavin + */ +class ZookeeperDesk(deskMap: Storage, + ft: ControlTypes, + hType: ControlType[Holder] = HolderControlType) + extends HoldersDesk[Future](ft, hType) { + + override def list: Future[Seq[Control[Holder]]] = + Future.fromTry( + for { + keys <- deskMap.keys + result <- keys.foldLeft(Success(Seq.empty) : Try[Seq[Control[Holder]]]) { + case (f@Failure(_), _) => f + case (Success(s), k) => + val nc = for { + optV <- deskMap.get(k) + v <- Try(optV.getOrElse(throw new NoSuchElementException(s"No $k"))) + } yield Control(k, _ => hType.from(v).get) + nc.map(ch => s :+ ch) + } + } yield result + ) + + override def delete(name: String): Future[Boolean] = + Future.fromTry(deskMap.remove(name)) + + override protected def registerInner[V](name: String, + init: String): Control[V] = + registrationLock.synchronized { // do registrations sequential due of zk performance + (for { + exist <- deskMap.get(name) + _ <- exist match { + case None => deskMap.set(name, init) + case _ => Success(()) + } + } yield Control[V](name, n => decode[V](deskMap.get(n).get.get))) // TODO + .get + } + + protected def updateInner(name: String) + (updater: String => String): Future[Unit] = + Future.fromTry { + for { + optExist <- deskMap.get(name) + exist <- Try(optExist.getOrElse(throw new NoSuchElementException(s"No $name"))) + _ <- deskMap.set(name, updater(exist)) + } yield () + } +} + +object ZookeeperDesk { + + private val registrationLock = new Object +} diff --git a/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/ZookeeperStorage.scala b/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/ZookeeperStorage.scala new file mode 100644 index 0000000..2115b76 --- /dev/null +++ b/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/ZookeeperStorage.scala @@ -0,0 +1,61 @@ +package ru.alesavin.cockpit.impl + +import org.apache.curator.framework.CuratorFramework +import org.apache.curator.utils.ZKPaths +import org.apache.zookeeper.{CreateMode, KeeperException} +import ru.alesavin.cockpit.impl.ZookeeperStorage._ + +import scala.util.Try +import scala.collection.JavaConverters._ + + +/** + * TODO + * + * @author alesavin + */ +class ZookeeperStorage(client: CuratorFramework, + baseZkPath: String) extends Storage { + + require(baseZkPath.nonEmpty, "Empty zookeeper path") + + override def keys: Try[Iterable[String]] = + Try(client.getChildren.forPath(baseZkPath).asScala) + + override def get(key: String): Try[Option[String]] = + Try { + Option(client.getData.forPath(ZKPaths.makePath(baseZkPath, key))).map(to) + }.recover { + case _: KeeperException.NoNodeException => None + } + + override def set(key: String, value: String): Try[Unit] = + Try { + client + .create() + .creatingParentsIfNeeded() + .withMode(CreateMode.PERSISTENT) + .forPath(ZKPaths.makePath(baseZkPath, key), from(value)) + () + }.recover { + case _: KeeperException.NodeExistsException => + client + .setData() + .forPath(ZKPaths.makePath(baseZkPath, key), from(value)) + () + } + + override def remove(key: String): Try[Boolean] = + Try { + client.delete().forPath(ZKPaths.makePath(baseZkPath, key)) + true + }.recover { + case _: KeeperException.NoNodeException => false + } +} + +object ZookeeperStorage { + + def to(data: Array[Byte]): String = new String(data, "UTF-8") + def from(data: String): Array[Byte] = data.getBytes("UTF-8") +} \ No newline at end of file diff --git a/zookeeper/src/test/scala/ru/alesavin/cockpit/impl/ZookeeperDeskSpec.scala b/zookeeper/src/test/scala/ru/alesavin/cockpit/impl/ZookeeperDeskSpec.scala new file mode 100644 index 0000000..d6edb6f --- /dev/null +++ b/zookeeper/src/test/scala/ru/alesavin/cockpit/impl/ZookeeperDeskSpec.scala @@ -0,0 +1,36 @@ +package ru.alesavin.cockpit.impl + +import org.apache.curator.framework.{CuratorFramework, CuratorFrameworkFactory} +import org.apache.curator.retry.ExponentialBackoffRetry +import org.apache.curator.test.TestingCluster +import ru.alesavin.cockpit.model.{ControlTypes, Desk} + +import scala.concurrent.Future + +/** + * TODO + * + * @author alesavin + */ +class ZookeeperDeskSpec + extends DeskSpecBase { + + def desk(ft: ControlTypes): Desk[Future] = { + val zkCluster = new TestingCluster(3) + zkCluster.start() + + val Curator: CuratorFramework = { + val curatorFramework = CuratorFrameworkFactory.newClient( + zkCluster.getConnectString, + new ExponentialBackoffRetry(100, 3)) + curatorFramework.start() + curatorFramework + } + val BasePath = "/test" + Curator.create().creatingParentsIfNeeded().forPath(BasePath) + + val zkStorage = + new ZookeeperStorage(Curator, BasePath) + new ZookeeperDesk(zkStorage, ft) + } +} diff --git a/zookeeper/src/test/scala/ru/alesavin/cockpit/impl/ZookeeperStorageSpec.scala b/zookeeper/src/test/scala/ru/alesavin/cockpit/impl/ZookeeperStorageSpec.scala new file mode 100644 index 0000000..9fb1985 --- /dev/null +++ b/zookeeper/src/test/scala/ru/alesavin/cockpit/impl/ZookeeperStorageSpec.scala @@ -0,0 +1,100 @@ +package ru.alesavin.cockpit.impl + +import org.apache.curator.framework.{CuratorFramework, CuratorFrameworkFactory} +import org.apache.curator.retry.ExponentialBackoffRetry +import org.apache.zookeeper.KeeperException +import org.scalatest.{Matchers, WordSpec} + +import scala.util.{Failure, Success} + +/** + * TODO + * + * @author alesavin + */ +class ZookeeperStorageSpec + extends WordSpec + with Matchers { + + import org.apache.curator.test.TestingCluster + + private val zkCluster = new TestingCluster(3) + zkCluster.start() + + "ZookeeperStorage" should { + + val Curator: CuratorFramework = { + val curatorFramework = CuratorFrameworkFactory.newClient( + zkCluster.getConnectString, + new ExponentialBackoffRetry(100, 3)) + curatorFramework.start() + curatorFramework + } + val BasePath = "/test" + val zkStorage = + new ZookeeperStorage(Curator, BasePath) + + "fail if have no base path" in { + zkStorage.keys match { + case Failure(_ : KeeperException.NoNodeException) => info("Done") + case other => fail(s"Unexpected $other") + } + } + "list keys" in { + Curator.create().creatingParentsIfNeeded().forPath(BasePath) + zkStorage.keys match { + case Success(ks) if ks.isEmpty => info("Done") + case other => fail(s"Unexpected $other") + } + } + "get non-exist key" in { + zkStorage.get("k") match { + case Success(None) => info("Done") + case other => fail(s"Unexpected $other") + } + } + "set data for new key" in { + zkStorage.set("k", "v") match { + case Success(()) => info("Done") + case other => fail(s"Unexpected $other") + } + zkStorage.get("k") match { + case Success(Some("v")) => info("Done") + case other => fail(s"Unexpected $other") + } + } + "update data for key" in { + zkStorage.get("k") match { + case Success(Some("v")) => info("Done") + case other => fail(s"Unexpected $other") + } + zkStorage.set("k", "v2") match { + case Success(()) => info("Done") + case other => fail(s"Unexpected $other") + } + zkStorage.get("k") match { + case Success(Some("v2")) => info("Done") + case other => fail(s"Unexpected $other") + } + } + "remove keys" in { + zkStorage.remove("k2") match { + case Success(false) => info("Done") + case other => fail(s"Unexpected $other") + } + zkStorage.get("k") match { + case Success(Some("v2")) => info("Done") + case other => fail(s"Unexpected $other") + } + zkStorage.remove("k") match { + case Success(true) => info("Done") + case other => fail(s"Unexpected $other") + } + zkStorage.get("k") match { + case Success(None) => info("Done") + case other => fail(s"Unexpected $other") + } + } + } +} + From 1a968784f1d92318b3df8d4120d489d2b5d728e3 Mon Sep 17 00:00:00 2001 From: alesavin Date: Mon, 22 Apr 2019 22:30:12 +0200 Subject: [PATCH 2/2] resolve #4: add caching for zkStorage; add logback dep --- build.sbt | 2 + .../alesavin/cockpit/impl/CachedStorage.scala | 40 +++++++++++++++++++ .../ru/alesavin/cockpit/impl/Storage.scala | 3 +- .../alesavin/cockpit/impl/ZookeeperDesk.scala | 32 ++++++++++----- .../cockpit/impl/ZookeeperStorage.scala | 2 +- zookeeper/src/test/resources/logback-test.xml | 12 ++++++ .../cockpit/impl/ZookeeperDeskSpec.scala | 6 +-- .../cockpit/impl/ZookeeperStorageSpec.scala | 2 +- 8 files changed, 81 insertions(+), 18 deletions(-) create mode 100644 zookeeper/src/main/scala/ru/alesavin/cockpit/impl/CachedStorage.scala create mode 100644 zookeeper/src/test/resources/logback-test.xml diff --git a/build.sbt b/build.sbt index 7414cd8..de0873a 100644 --- a/build.sbt +++ b/build.sbt @@ -13,6 +13,7 @@ conflictManager := ConflictManager.strict val JunitVersion = "4.12" val ScalaTestVersion = "3.0.5" +val LogbackVersion = "1.2.3" val CuratorVersion = "4.2.0" val customScalacOptions = Seq( @@ -47,6 +48,7 @@ lazy val zookeeper = (project in file("zookeeper")) name := "cockpit-zookeeper", scalacOptions ++= customScalacOptions, libraryDependencies ++= customDependencies ++ Seq( + "ch.qos.logback" % "logback-classic" % LogbackVersion, "org.apache.curator" % "curator-framework" % CuratorVersion, "org.apache.curator" % "curator-test" % CuratorVersion % Test, ) diff --git a/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/CachedStorage.scala b/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/CachedStorage.scala new file mode 100644 index 0000000..6549ea9 --- /dev/null +++ b/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/CachedStorage.scala @@ -0,0 +1,40 @@ +package ru.alesavin.cockpit.impl + +import com.google.common.cache.CacheBuilder + +import scala.concurrent.duration.FiniteDuration +import scala.util.{Success, Try} + +/** + * Caching mix-in for [[Storage]] + * + * @author alesavin + */ +trait CachedStorage extends Storage { + + def duration: FiniteDuration + + private val Cache: com.google.common.cache.Cache[String, String] = + CacheBuilder.newBuilder() + .expireAfterWrite(duration.length, duration.unit) + .build() + + abstract override def get(key: String): Try[Option[String]] = + for { + cached <- Try(Option(Cache.getIfPresent(key))) + r <- cached match { + case s@Some(_) => Success(s) + case _ => super.get(key).map { v => + v.foreach(Cache.put(key, _)) + v + } + } + } yield r + + + abstract override def set(key: String, value: String): Try[Unit] = + for { + _ <- Try(Cache.invalidate(key)) + r <- super.set(key, value) + } yield r +} diff --git a/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/Storage.scala b/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/Storage.scala index 6b45e33..72fe98f 100644 --- a/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/Storage.scala +++ b/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/Storage.scala @@ -3,7 +3,8 @@ package ru.alesavin.cockpit.impl import scala.util.Try /** - * TODO + * Provide simple interface to storage + * TODO move to core * * @author alesavin */ diff --git a/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/ZookeeperDesk.scala b/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/ZookeeperDesk.scala index 85531aa..534f195 100644 --- a/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/ZookeeperDesk.scala +++ b/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/ZookeeperDesk.scala @@ -2,32 +2,42 @@ package ru.alesavin.cockpit.impl import java.util.NoSuchElementException +import org.apache.curator.framework.CuratorFramework import ru.alesavin.cockpit.impl.HoldersDesk.HolderControlType import ru.alesavin.cockpit.impl.ZookeeperDesk.registrationLock import ru.alesavin.cockpit.model.{Control, ControlType, ControlTypes, Holder} import scala.concurrent.Future +import scala.concurrent.duration._ import scala.util.{Failure, Success, Try} /** - * TODO + * Impl of [[ru.alesavin.cockpit.model.Desk]] over Zookeeper * * @author alesavin */ -class ZookeeperDesk(deskMap: Storage, +class ZookeeperDesk(client: CuratorFramework, + baseZkPath: String, ft: ControlTypes, - hType: ControlType[Holder] = HolderControlType) + hType: ControlType[Holder] = HolderControlType, + cacheDuration: FiniteDuration = 30.seconds) extends HoldersDesk[Future](ft, hType) { + private val zkStorage = + new ZookeeperStorage(client, baseZkPath) + with CachedStorage { + override def duration: FiniteDuration = cacheDuration + } + override def list: Future[Seq[Control[Holder]]] = Future.fromTry( for { - keys <- deskMap.keys + keys <- zkStorage.keys result <- keys.foldLeft(Success(Seq.empty) : Try[Seq[Control[Holder]]]) { case (f@Failure(_), _) => f case (Success(s), k) => val nc = for { - optV <- deskMap.get(k) + optV <- zkStorage.get(k) v <- Try(optV.getOrElse(throw new NoSuchElementException(s"No $k"))) } yield Control(k, _ => hType.from(v).get) nc.map(ch => s :+ ch) @@ -36,18 +46,18 @@ class ZookeeperDesk(deskMap: Storage, ) override def delete(name: String): Future[Boolean] = - Future.fromTry(deskMap.remove(name)) + Future.fromTry(zkStorage.remove(name)) override protected def registerInner[V](name: String, init: String): Control[V] = registrationLock.synchronized { // do registrations sequential due of zk performance (for { - exist <- deskMap.get(name) + exist <- zkStorage.get(name) _ <- exist match { - case None => deskMap.set(name, init) + case None => zkStorage.set(name, init) case _ => Success(()) } - } yield Control[V](name, n => decode[V](deskMap.get(n).get.get))) // TODO + } yield Control[V](name, n => decode[V](zkStorage.get(n).get.get))) // TODO .get } @@ -55,9 +65,9 @@ class ZookeeperDesk(deskMap: Storage, (updater: String => String): Future[Unit] = Future.fromTry { for { - optExist <- deskMap.get(name) + optExist <- zkStorage.get(name) exist <- Try(optExist.getOrElse(throw new NoSuchElementException(s"No $name"))) - _ <- deskMap.set(name, updater(exist)) + _ <- zkStorage.set(name, updater(exist)) } yield () } } diff --git a/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/ZookeeperStorage.scala b/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/ZookeeperStorage.scala index 2115b76..fb65fd1 100644 --- a/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/ZookeeperStorage.scala +++ b/zookeeper/src/main/scala/ru/alesavin/cockpit/impl/ZookeeperStorage.scala @@ -10,7 +10,7 @@ import scala.collection.JavaConverters._ /** - * TODO + * Impl of [[Storage]] over Zookeeper * * @author alesavin */ diff --git a/zookeeper/src/test/resources/logback-test.xml b/zookeeper/src/test/resources/logback-test.xml new file mode 100644 index 0000000..99a16b2 --- /dev/null +++ b/zookeeper/src/test/resources/logback-test.xml @@ -0,0 +1,12 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + \ No newline at end of file diff --git a/zookeeper/src/test/scala/ru/alesavin/cockpit/impl/ZookeeperDeskSpec.scala b/zookeeper/src/test/scala/ru/alesavin/cockpit/impl/ZookeeperDeskSpec.scala index d6edb6f..1301cbf 100644 --- a/zookeeper/src/test/scala/ru/alesavin/cockpit/impl/ZookeeperDeskSpec.scala +++ b/zookeeper/src/test/scala/ru/alesavin/cockpit/impl/ZookeeperDeskSpec.scala @@ -8,7 +8,7 @@ import ru.alesavin.cockpit.model.{ControlTypes, Desk} import scala.concurrent.Future /** - * TODO + * Specd on [[ZookeeperDesk]] * * @author alesavin */ @@ -29,8 +29,6 @@ class ZookeeperDeskSpec val BasePath = "/test" Curator.create().creatingParentsIfNeeded().forPath(BasePath) - val zkStorage = - new ZookeeperStorage(Curator, BasePath) - new ZookeeperDesk(zkStorage, ft) + new ZookeeperDesk(Curator, BasePath, ft) } } diff --git a/zookeeper/src/test/scala/ru/alesavin/cockpit/impl/ZookeeperStorageSpec.scala b/zookeeper/src/test/scala/ru/alesavin/cockpit/impl/ZookeeperStorageSpec.scala index 9fb1985..167f859 100644 --- a/zookeeper/src/test/scala/ru/alesavin/cockpit/impl/ZookeeperStorageSpec.scala +++ b/zookeeper/src/test/scala/ru/alesavin/cockpit/impl/ZookeeperStorageSpec.scala @@ -8,7 +8,7 @@ import org.scalatest.{Matchers, WordSpec} import scala.util.{Failure, Success} /** - * TODO + * Specs on [[ZookeeperStorage]] * * @author alesavin */