Skip to content

Commit

Permalink
Implement backwards compatible API
Browse files Browse the repository at this point in the history
So that ota-app can use both directors

Signed-off-by: Simão Mata <[email protected]>
  • Loading branch information
simao committed Jan 28, 2020
1 parent 4c64db1 commit c4529f8
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,22 @@ import akka.http.scaladsl.server._
import cats.syntax.option._
import com.advancedtelematic.director.data.AdminDataType.{FindImageCount, RegisterDevice}
import com.advancedtelematic.director.data.Codecs._
import com.advancedtelematic.director.db.{AutoUpdateDefinitionRepositorySupport, DeviceRegistration, DeviceRepositorySupport, EcuRepositorySupport, RepoNamespaceRepositorySupport}
import com.advancedtelematic.director.db.{AutoUpdateDefinitionRepositorySupport, DeviceRegistration, EcuRepositorySupport, RepoNamespaceRepositorySupport}
import com.advancedtelematic.libats.codecs.CirceCodecs._
import com.advancedtelematic.libats.data.DataType.Namespace
import com.advancedtelematic.libats.data.EcuIdentifier
import com.advancedtelematic.libats.http.UUIDKeyAkka._
import com.advancedtelematic.libats.messaging.MessageBusPublisher
import com.advancedtelematic.libats.messaging_datatype.DataType.{DeviceId, UpdateId}
import com.advancedtelematic.libats.messaging_datatype.DataType.DeviceId
import com.advancedtelematic.libtuf.data.ClientCodecs._
import com.advancedtelematic.libtuf.data.TufCodecs._
import com.advancedtelematic.libtuf.data.TufDataType.{Ed25519KeyType, RepoId, TargetName}
import com.advancedtelematic.libtuf.data.TufDataType.TargetName
import com.advancedtelematic.libtuf_server.keyserver.KeyserverClient
import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport._
import com.advancedtelematic.libats.codecs.CirceCodecs._
import slick.jdbc.MySQLProfile.api._

import scala.concurrent.{ExecutionContext, Future}

class RepositoryCreation(keyserverClient: KeyserverClient)(implicit val db: Database, val ec: ExecutionContext)
extends DeviceRepositorySupport with RepoNamespaceRepositorySupport {

def create(ns: Namespace): Future[Unit] = {
val repoId = RepoId.generate()
import scala.concurrent.ExecutionContext

for {
_ <- keyserverClient.createRoot(repoId, Ed25519KeyType, forceSync = true)
_ <- repoNamespaceRepo.persist(repoId, ns)
} yield ()
}
}

class AdminResource(extractNamespace: Directive1[Namespace], val keyserverClient: KeyserverClient)
(implicit val db: Database, val ec: ExecutionContext, messageBusPublisher: MessageBusPublisher)
Expand Down Expand Up @@ -95,10 +83,10 @@ class AdminResource(extractNamespace: Directive1[Namespace], val keyserverClient
}
}
} ~
(pathEnd & get) {
val f = deviceRegistration.findDeviceEcuInfo(ns, device)
complete(f)
}
(pathEnd & get) {
val f = deviceRegistration.findDeviceEcuInfo(ns, device)
complete(f)
}
}

val route: Route = extractNamespace { ns =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,17 @@ import java.time.Instant
import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.server._
import akka.http.scaladsl.unmarshalling.PredefinedFromStringUnmarshallers.CsvSeq
import akka.http.scaladsl.util.FastFuture
import com.advancedtelematic.director.data.AdminDataType.AssignUpdateRequest
import com.advancedtelematic.director.data.AssignmentDataType.CancelAssignments
import com.advancedtelematic.director.data.Codecs._
import com.advancedtelematic.director.data.DbDataType
import com.advancedtelematic.libats.data.DataType.Namespace
import com.advancedtelematic.libats.http.UUIDKeyAkka._
import com.advancedtelematic.libats.messaging.MessageBusPublisher
import com.advancedtelematic.libats.messaging_datatype.DataType.{DeviceId, UpdateId}
import com.advancedtelematic.libats.messaging_datatype.Messages.{DeviceUpdateAssigned, DeviceUpdateCanceled, DeviceUpdateEvent}
import com.advancedtelematic.libats.messaging_datatype.MessageCodecs.deviceUpdateCanceledEncoder
import com.advancedtelematic.libats.messaging_datatype.Messages.{DeviceUpdateAssigned, DeviceUpdateEvent}
import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport._
import slick.jdbc.MySQLProfile.api.Database
import com.advancedtelematic.libats.messaging_datatype.MessageCodecs.deviceUpdateCanceledEncoder

import scala.concurrent.{ExecutionContext, Future}

Expand Down Expand Up @@ -63,7 +61,7 @@ class AssignmentsResource(extractNamespace: Directive1[Namespace])
}
}
} ~
pathPrefix(DeviceId.Path) { deviceId =>
path(DeviceId.Path) { deviceId =>
get { // This should be replacing /queue in /admin
val f = deviceAssignments.findDeviceAssignments(ns, deviceId)
complete(f)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.advancedtelematic.director.http

import java.time.Instant

import akka.http.scaladsl.util.FastFuture
import cats.implicits._
import com.advancedtelematic.director.data.AdminDataType.QueueResponse
import com.advancedtelematic.director.data.DbDataType.Assignment
Expand Down Expand Up @@ -65,6 +66,10 @@ class DeviceAssignments(implicit val db: Database, val ec: ExecutionContext) ext
}
}

def createForDevice(ns: Namespace, correlationId: CorrelationId, deviceId: DeviceId, mtuId: UpdateId): Future[Assignment] = {
createForDevices(ns, correlationId, List(deviceId), mtuId).map(_.head)
}

def createForDevices(ns: Namespace, correlationId: CorrelationId, devices: Seq[DeviceId], mtuId: UpdateId): Future[Seq[Assignment]] = async {
val ecus = await(findAffectedEcus(ns, devices, mtuId))

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.advancedtelematic.director.http

import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.server.{Directives, _}
import com.advancedtelematic.libats.auth.NamespaceDirectives
import com.advancedtelematic.libats.http.DefaultRejectionHandler.rejectionHandler
import com.advancedtelematic.libats.http.ErrorHandler
import com.advancedtelematic.libats.messaging.MessageBusPublisher
import com.advancedtelematic.libats.messaging_datatype.DataType.{DeviceId, UpdateId}
import com.advancedtelematic.libtuf_server.keyserver.KeyserverClient
import slick.jdbc.MySQLProfile.api._

Expand All @@ -24,7 +26,8 @@ class DirectorRoutes(keyserverClient: KeyserverClient)
new AdminResource(extractNamespace, keyserverClient).route ~
new AssignmentsResource(extractNamespace).route ~
new DeviceResource(extractNamespace, keyserverClient).route ~
new MultiTargetUpdatesResource(extractNamespace).route
new MultiTargetUpdatesResource(extractNamespace).route ~
new LegacyRoutes(extractNamespace).route
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.advancedtelematic.director.http

import java.time.Instant

import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.server.Directives.{complete, delete, path, put}
import akka.http.scaladsl.server.{Directive1, Route}
import com.advancedtelematic.libats.data.DataType.{MultiTargetUpdateId, Namespace}
import com.advancedtelematic.libats.messaging.MessageBusPublisher
import com.advancedtelematic.libats.messaging_datatype.DataType.{DeviceId, UpdateId}
import com.advancedtelematic.libats.messaging_datatype.Messages.{DeviceUpdateAssigned, DeviceUpdateEvent}
import slick.jdbc.MySQLProfile.api._
import com.advancedtelematic.libats.http.UUIDKeyAkka._
import akka.http.scaladsl.server.Directives._
import scala.concurrent.{ExecutionContext, Future}
import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport._

// Implements routes provided by old director that ota-web-app still uses
class LegacyRoutes(extractNamespace: Directive1[Namespace])(implicit val db: Database, ec: ExecutionContext, messageBusPublisher: MessageBusPublisher) {
private val deviceAssignments = new DeviceAssignments()

private def createDeviceAssignment(ns: Namespace, deviceId: DeviceId, mtuId: UpdateId): Future[Unit] = {
val correlationId = MultiTargetUpdateId(mtuId.uuid)
val assignment = deviceAssignments.createForDevice(ns, correlationId, deviceId, mtuId)

assignment.map { a =>
val msg: DeviceUpdateEvent = DeviceUpdateAssigned(ns, Instant.now(), correlationId, a.deviceId)
messageBusPublisher.publishSafe(msg)
}
}

val route: Route =
extractNamespace { ns =>
path("admin" / "devices" / DeviceId.Path / "multi_target_update" / UpdateId.Path) { (deviceId, updateId) =>
put {
val f = createDeviceAssignment(ns, deviceId, updateId).map(_ => StatusCodes.Created)
complete(f)
}
} ~
path("assignments" / DeviceId.Path) { deviceId =>
delete {
val a = deviceAssignments.cancel(ns, List(deviceId))
complete(a.map(_.map(_.deviceId)))
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.advancedtelematic.director.http

import com.advancedtelematic.director.db.{DeviceRepositorySupport, RepoNamespaceRepositorySupport}
import com.advancedtelematic.libats.data.DataType.Namespace
import com.advancedtelematic.libtuf.data.TufDataType.{Ed25519KeyType, RepoId}
import com.advancedtelematic.libtuf_server.keyserver.KeyserverClient
import slick.jdbc.MySQLProfile.api._
import com.advancedtelematic.libats.http.UUIDKeyAkka._

import scala.concurrent.{ExecutionContext, Future}

class RepositoryCreation(keyserverClient: KeyserverClient)(implicit val db: Database, val ec: ExecutionContext)
extends DeviceRepositorySupport with RepoNamespaceRepositorySupport {

def create(ns: Namespace): Future[Unit] = {
val repoId = RepoId.generate()

for {
_ <- keyserverClient.createRoot(repoId, Ed25519KeyType, forceSync = true)
_ <- repoNamespaceRepo.persist(repoId, ns)
} yield ()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.advancedtelematic.director.http

import akka.http.scaladsl.model.StatusCodes
import com.advancedtelematic.director.util._
import com.advancedtelematic.libats.data.DataType.Namespace
import com.advancedtelematic.libats.data.DataType.{MultiTargetUpdateId, Namespace}
import com.advancedtelematic.libats.messaging_datatype.DataType.{DeviceId, UpdateId}
import com.advancedtelematic.director.data.Generators._
import com.advancedtelematic.director.data.GeneratorOps._
Expand All @@ -19,11 +19,12 @@ import com.advancedtelematic.libtuf.data.TufDataType.{HardwareIdentifier, Signed
import com.advancedtelematic.libtuf.data.ClientCodecs._
import com.advancedtelematic.libtuf.data.TufCodecs._
import cats.syntax.show._
import com.advancedtelematic.director.data.AdminDataType.{EcuInfoResponse, FindImageCount, RegisterDevice}
import com.advancedtelematic.director.data.AdminDataType.{EcuInfoResponse, FindImageCount, MultiTargetUpdate, QueueResponse, RegisterDevice}
import org.scalactic.source.Position
import com.advancedtelematic.director.data.Codecs._
import com.advancedtelematic.director.data.DeviceRequest.{DeviceManifest, InstallationReportEntity}
import com.advancedtelematic.libats.codecs.CirceCodecs._
import org.scalatest.OptionValues._
import com.advancedtelematic.libats.messaging_datatype.Messages._

object AdminResources {
case class RegisterDeviceResult(deviceId: DeviceId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,9 @@ class AssignmentsResourceSpec extends DirectorSpec
createAssignmentOk(regDev0.deviceId, regDev0.primary.hardwareId)

val queue0 = getDeviceAssignmentOk(regDev0.deviceId)

queue0 shouldNot be(empty)

val queue1 = getDeviceAssignmentOk(regDev1.deviceId)

queue1 shouldBe empty
}

Expand Down Expand Up @@ -184,6 +182,9 @@ class AssignmentsResourceSpec extends DirectorSpec

cancelAssignmentsOk(Seq(regDev.deviceId)) shouldBe Seq(regDev.deviceId)

val queue = getDeviceAssignmentOk(regDev.deviceId)
queue shouldBe empty

val msg = msgPub.wasReceived[DeviceUpdateEvent] { msg: DeviceUpdateEvent =>
msg.deviceUuid == regDev.deviceId
}
Expand All @@ -192,7 +193,7 @@ class AssignmentsResourceSpec extends DirectorSpec
msg.get shouldBe a [DeviceUpdateCanceled]
}

testWithRepo("DELETE assignments can only cancel if update is not in-flight") { implicit ns =>
testWithRepo("PATCH assignments can only cancel if update is not in-flight") { implicit ns =>
val regDev = registerAdminDeviceOk()
createAssignmentOk(regDev.deviceId, regDev.primary.hardwareId)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.advancedtelematic.director.http

import akka.http.scaladsl.model.StatusCodes
import com.advancedtelematic.director.data.AdminDataType.{MultiTargetUpdate, QueueResponse}
import com.advancedtelematic.director.util.{DirectorSpec, MockMessageBus, RepositorySpec, RouteResourceSpec}
import com.advancedtelematic.libats.messaging_datatype.DataType.{DeviceId, UpdateId}
import com.advancedtelematic.director.data.Generators._
import com.advancedtelematic.libats.data.DataType.MultiTargetUpdateId
import com.advancedtelematic.director.data.GeneratorOps._
import com.advancedtelematic.director.data.Codecs._
import com.advancedtelematic.libats.codecs.CirceCodecs._
import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport._
import cats.syntax.show._
import org.scalatest.OptionValues._
import com.advancedtelematic.libats.messaging_datatype.Messages._

class LegacyApiResourceSpec extends DirectorSpec
with RouteResourceSpec
with AdminResources
with RepositorySpec
with AssignmentResources {

override implicit val msgPub = new MockMessageBus

testWithRepo("creates an assignment for the given update id for the specified device") { implicit ns =>
val regDev = registerAdminDeviceWithSecondariesOk()

val targetUpdate = GenTargetUpdateRequest.generate
val mtu = MultiTargetUpdate(Map(regDev.primary.hardwareId -> targetUpdate))

val mtuId = Post(apiUri("multi_target_updates"), mtu).namespaced ~> routes ~> check {
status shouldBe StatusCodes.Created
responseAs[UpdateId]
}

Put(apiUri(s"admin/devices/${regDev.deviceId.show}/multi_target_update/${mtuId.show}")).namespaced ~> routes ~> check {
status shouldBe StatusCodes.Created
}

val queue = Get(apiUri(s"assignments/${regDev.deviceId.show}")).namespaced ~> routes ~> check {
status shouldBe StatusCodes.OK
responseAs[List[QueueResponse]]
}

queue.head.correlationId shouldBe MultiTargetUpdateId(mtuId.uuid)
queue.head.targets.get(regDev.primary.ecuSerial).value.image.filepath shouldBe targetUpdate.to.target
queue.head.targets.get(regDev.secondaries.keys.head) shouldBe empty

val msg = msgPub.wasReceived[DeviceUpdateEvent] { msg: DeviceUpdateEvent =>
msg.deviceUuid == regDev.deviceId
}

msg.value shouldBe a [DeviceUpdateAssigned]
}

testWithRepo("DELETE assignments cancels assigned updates") { implicit ns =>
val regDev = registerAdminDeviceOk()
createAssignmentOk(regDev.deviceId, regDev.primary.hardwareId)

val queue0 = getDeviceAssignmentOk(regDev.deviceId)
queue0 shouldNot be(empty)

Delete(apiUri("assignments/" + regDev.deviceId.show)).namespaced ~> routes ~> check {
status shouldBe StatusCodes.OK
responseAs[Seq[DeviceId]]
}

val queue = getDeviceAssignmentOk(regDev.deviceId)
queue shouldBe empty

val msg = msgPub.wasReceived[DeviceUpdateEvent] { msg: DeviceUpdateEvent =>
msg.deviceUuid == regDev.deviceId
}

msg shouldBe defined
msg.get shouldBe a [DeviceUpdateCanceled]
}
}

0 comments on commit c4529f8

Please sign in to comment.