Skip to content

Commit 92b3e57

Browse files
authored
Merge pull request #3 from uptane/trx-master
Latest changes from trx/master
2 parents d571e8d + f4cbb97 commit 92b3e57

File tree

10 files changed

+191
-5
lines changed

10 files changed

+191
-5
lines changed

.gitlab-ci-toradex.yml

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
stages:
2+
- test
3+
- build image
4+
- deploy
5+
6+
variables:
7+
DOCKER_TLS_CERTDIR: ""
8+
DOCKER_HOST: tcp://localhost:2375
9+
DOCKER_DRIVER: overlay2
10+
MYSQL_ROOT_PASSWORD: "root"
11+
MYSQL_DATABASE: "ota_treehub"
12+
MYSQL_USER: "treehub"
13+
MYSQL_PASSWORD: "treehub"
14+
SBT_OPTS: "-sbt-launch-dir .sbt/launchers -sbt-dir .sbt -ivy .ivy2 -Dsbt.color=true -Dscala.color=true"
15+
ARTIFACTORY_USERNAME: cicd-innovation
16+
ARTIFACTORY_URL: https://artifactory-horw.int.toradex.com/artifactory
17+
SERVICE: treehub
18+
19+
image: artifactory-horw.int.toradex.com/ota-docker-dev-horw/tdx-inno-ci-scala:0.0.1
20+
21+
services:
22+
- name: docker:dind
23+
- name: mariadb:10.4
24+
alias: db
25+
command:
26+
- --character-set-server=utf8
27+
- --collation-server=utf8_unicode_ci
28+
- --max_connections=1000
29+
30+
default:
31+
cache:
32+
key: treehub
33+
when: always
34+
paths:
35+
- .ivy2/
36+
- .sbt/
37+
38+
test:
39+
stage: test
40+
only:
41+
refs:
42+
- master
43+
- branches
44+
- merge_requests
45+
variables:
46+
DB_URL: "jdbc:mariadb://db:3306/ota_treehub"
47+
before_script:
48+
- echo "GRANT ALL PRIVILEGES ON \`ota\_treehub%\`.* TO 'treehub'@'%'; FLUSH PRIVILEGES;" > db_user.sql
49+
- until mysqladmin ping --protocol=TCP -h db -P 3306 -u root -proot; do echo waiting for mysql; sleep 1; done
50+
- mysql -v -h db -u root -proot < db_user.sql
51+
script:
52+
- sbt -J-Xmx1G -J-XX:MaxPermSize=256m -J-XX:ReservedCodeCacheSize=128m ut:test
53+
54+
include:
55+
- project: rd/innovation/continuous-delivery/ci-container-build
56+
file: gitlab-ci-templates/jfrog-sbt-docker-publish.yaml
57+
- project: 'rd/platform/deploy-env'
58+
ref: master
59+
file: '/gitlab/deploy-core-service.yml'
60+
61+
build docker image:
62+
stage: build image
63+
only:
64+
refs:
65+
- master
66+
- merge_requests
67+
extends: .jfrog-sbt-docker-publish
68+
variables:
69+
ARTIFACTORY_REPO: ota-docker-dev-horw
70+
SERVICE_NAME: treehub
71+
72+

src/main/scala/com/advancedtelematic/treehub/http/ObjectResource.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ class ObjectResource(namespace: Directive1[Namespace],
6969

7070
complete(f)
7171
} ~
72+
(delete & hintNamespaceStorage(ns)) {
73+
complete(objectStore.deleteObject(ns, objectId).map(_ => StatusCodes.NoContent))
74+
} ~
7275
(put & hintNamespaceStorage(ns)) {
7376
complete(objectStore.completeClientUpload(ns, objectId).map(_ => StatusCodes.NoContent))
7477
} ~

src/main/scala/com/advancedtelematic/treehub/object_store/BlobStore.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.advancedtelematic.treehub.object_store
22

3+
import akka.Done
34
import akka.http.scaladsl.model._
45
import akka.stream.scaladsl.Source
56
import akka.util.ByteString
@@ -16,6 +17,8 @@ object BlobStore {
1617
}
1718

1819
trait BlobStore {
20+
def deleteObject(ns: Namespace, objectId: ObjectId): Future[Done]
21+
1922
def storeStream(namespace: Namespace, id: ObjectId, size: Long, blob: Source[ByteString, _]): Future[Long]
2023

2124
val supportsOutOfBandStorage: Boolean

src/main/scala/com/advancedtelematic/treehub/object_store/LocalFsBlobStore.scala

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package com.advancedtelematic.treehub.object_store
22

3+
import akka.Done
4+
35
import java.nio.file.StandardOpenOption.{CREATE, READ, WRITE}
46
import java.nio.file.{Files, Path}
5-
67
import akka.http.scaladsl.model.HttpResponse
78
import akka.http.scaladsl.util.FastFuture
89
import akka.stream.Materializer
@@ -16,7 +17,6 @@ import org.slf4j.LoggerFactory
1617
import scala.concurrent.{ExecutionContext, Future}
1718
import scala.util.Try
1819

19-
2020
object LocalFsBlobStore {
2121
private val _log = LoggerFactory.getLogger(this.getClass)
2222

@@ -92,4 +92,13 @@ class LocalFsBlobStore(root: Path)(implicit ec: ExecutionContext, mat: Materiali
9292
}
9393

9494
override val supportsOutOfBandStorage: Boolean = false
95+
96+
override def deleteObject(ns: Namespace, objectId: ObjectId): Future[Done] = {
97+
val f = Try {
98+
Files.delete(objectId.path(namespacePath(ns)))
99+
Done
100+
}
101+
102+
Future.fromTry(f)
103+
}
95104
}

src/main/scala/com/advancedtelematic/treehub/object_store/ObjectStore.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
package com.advancedtelematic.treehub.object_store
22

3-
import java.io.File
3+
import akka.Done
44

5+
import java.io.File
56
import akka.http.scaladsl.model.HttpResponse
67
import akka.http.scaladsl.util.FastFuture
78
import akka.stream.scaladsl.{FileIO, Source}
@@ -16,6 +17,10 @@ import slick.jdbc.MySQLProfile.api._
1617
import scala.concurrent.{ExecutionContext, Future}
1718

1819
class ObjectStore(blobStore: BlobStore)(implicit ec: ExecutionContext, db: Database) extends ObjectRepositorySupport {
20+
def deleteObject(ns: Namespace, objectId: ObjectId): Future[Done] = for {
21+
deleted <- objectRepository.delete(ns, objectId)
22+
_ <- if(deleted > 0) blobStore.deleteObject(ns, objectId) else FastFuture.failed(Errors.ObjectNotFound)
23+
} yield Done
1924

2025
import scala.async.Async._
2126

src/main/scala/com/advancedtelematic/treehub/object_store/S3BlobStore.scala

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
package com.advancedtelematic.treehub.object_store
22

3+
import akka.Done
4+
35
import java.io.File
46
import java.nio.file.Paths
57
import java.time.temporal.ChronoUnit
68
import java.time.{Duration, Instant}
79
import java.util.Date
8-
910
import akka.http.scaladsl.model.headers.Location
1011
import akka.http.scaladsl.model.{HttpResponse, StatusCodes, Uri}
1112
import akka.stream.Materializer
@@ -144,6 +145,14 @@ class S3BlobStore(s3Credentials: S3Credentials, s3client: AmazonS3, allowRedirec
144145
objectId.path(Paths.get(namespaceDir(namespace))).toString
145146

146147
override val supportsOutOfBandStorage: Boolean = true
148+
149+
override def deleteObject(ns: Namespace, objectId: ObjectId): Future[Done] =
150+
Future {
151+
blocking {
152+
s3client.deleteObject(bucketId, objectFilename(ns, objectId))
153+
Done
154+
}
155+
}
147156
}
148157

149158
object S3Client {

src/test/scala/com/advancedtelematic/treehub/http/ObjectResourceSpec.scala

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package com.advancedtelematic.treehub.http
77
import akka.http.scaladsl.model._
88
import akka.http.scaladsl.unmarshalling.PredefinedFromEntityUnmarshallers.byteArrayUnmarshaller
99
import akka.pattern.ask
10+
import com.advancedtelematic.libats.http.Errors.MissingEntity
1011
import com.advancedtelematic.treehub.db.ObjectRepositorySupport
1112
import com.advancedtelematic.util.FakeUsageUpdate.{CurrentBandwith, CurrentStorage}
1213
import com.advancedtelematic.util.ResourceSpec.ClientTObject
@@ -43,6 +44,71 @@ class ObjectResourceSpec extends TreeHubSpec with ResourceSpec with ObjectReposi
4344
}
4445
}
4546

47+
test("DELETE removes object") {
48+
val obj = new ClientTObject()
49+
50+
Post(apiUri(s"objects/${obj.prefixedObjectId}"), obj.blob) ~> routes ~> check {
51+
status shouldBe StatusCodes.NoContent
52+
}
53+
54+
Delete(apiUri(s"objects/${obj.prefixedObjectId}")) ~> routes ~> check {
55+
status shouldBe StatusCodes.NoContent
56+
}
57+
58+
Get(apiUri(s"objects/${obj.prefixedObjectId}")) ~> routes ~> check {
59+
status shouldBe StatusCodes.NotFound
60+
}
61+
}
62+
63+
test("DELETE removes object from database and underlying storage") {
64+
val obj = new ClientTObject()
65+
66+
Post(apiUri(s"objects/${obj.prefixedObjectId}"), obj.blob) ~> routes ~> check {
67+
status shouldBe StatusCodes.NoContent
68+
}
69+
70+
Delete(apiUri(s"objects/${obj.prefixedObjectId}")) ~> routes ~> check {
71+
status shouldBe StatusCodes.NoContent
72+
}
73+
74+
objectRepository.find(defaultNs, obj.objectId).failed.futureValue shouldBe Errors.ObjectNotFound
75+
objectStore.exists(defaultNs, obj.objectId).futureValue shouldBe false
76+
localFsBlobStore.exists(defaultNs, obj.objectId).futureValue shouldBe false
77+
}
78+
79+
test("DELETE keeps other objects intact") {
80+
val obj = new ClientTObject()
81+
val obj2 = new ClientTObject()
82+
83+
Post(apiUri(s"objects/${obj.prefixedObjectId}"), obj.blob) ~> routes ~> check {
84+
status shouldBe StatusCodes.NoContent
85+
}
86+
87+
Post(apiUri(s"objects/${obj2.prefixedObjectId}"), obj.blob) ~> routes ~> check {
88+
status shouldBe StatusCodes.NoContent
89+
}
90+
91+
Delete(apiUri(s"objects/${obj.prefixedObjectId}")) ~> routes ~> check {
92+
status shouldBe StatusCodes.NoContent
93+
}
94+
95+
Get(apiUri(s"objects/${obj.prefixedObjectId}")) ~> routes ~> check {
96+
status shouldBe StatusCodes.NotFound
97+
}
98+
99+
Get(apiUri(s"objects/${obj2.prefixedObjectId}")) ~> routes ~> check {
100+
status shouldBe StatusCodes.OK
101+
}
102+
}
103+
104+
test("DELETE returns 404 when object does not exist") {
105+
val obj = new ClientTObject()
106+
107+
Delete(apiUri(s"objects/${obj.prefixedObjectId}")) ~> routes ~> check {
108+
status shouldBe StatusCodes.NotFound
109+
}
110+
}
111+
46112
test("POST fails if object is empty and server does not support out of band upload") {
47113
val obj = new ClientTObject()
48114

src/test/scala/com/advancedtelematic/treehub/http/TreehubRoutesSpec.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.advancedtelematic.treehub.http
22

3+
import akka.Done
34
import akka.http.scaladsl.model.{HttpResponse, StatusCodes}
45
import akka.http.scaladsl.server.Directives
56
import akka.http.scaladsl.util.FastFuture
@@ -28,6 +29,8 @@ class TreehubRoutesSpec extends TreeHubSpec with ResourceSpec {
2829
override def readFull(namespace: DataType.Namespace, id: ObjectId): Future[ByteString] = ???
2930

3031
override def exists(namespace: DataType.Namespace, id: ObjectId): Future[Boolean] = FastFuture.failed(new SdkClientException("Timeout on waiting"))
32+
33+
override def deleteObject(ns: DataType.Namespace, objectId: ObjectId): Future[Done] = FastFuture.failed(new RuntimeException("[test] delete failed"))
3134
}
3235

3336
val errorObjectStore = new ObjectStore(errorBlobStore)

src/test/scala/com/advancedtelematic/treehub/object_store/S3BlobStoreIntegrationSpec.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,20 @@ class S3BlobStoreIntegrationSpec extends TreeHubSpec {
5656
s3BlobStore.exists(ns, obj.objectId).futureValue shouldBe true
5757
}
5858

59+
test("can delete object") {
60+
val obj = new ClientTObject()
61+
62+
val f = async {
63+
await(s3BlobStore.storeStream(ns, obj.objectId, obj.blob.size, obj.byteSource))
64+
}
65+
66+
s3BlobStore.exists(ns, obj.objectId).futureValue shouldBe true
67+
68+
s3BlobStore.deleteObject(ns, obj.objectId).futureValue
69+
70+
s3BlobStore.exists(ns, obj.objectId).futureValue shouldBe false
71+
}
72+
5973
test("build response builds a redirect") {
6074
val redirectS3BlobStore = S3BlobStore(s3Credentials, allowRedirects = true)
6175

src/test/scala/com/advancedtelematic/util/ResourceSpec.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,9 @@ trait ResourceSpec extends ScalatestRouteTest with DatabaseSpec with Settings {
8989

9090
lazy val namespaceExtractor = NamespaceDirectives.defaultNamespaceExtractor
9191

92-
val objectStore = new ObjectStore(new LocalFsBlobStore(Files.createTempDirectory("treehub-obj")))
92+
val localFsBlobStore = new LocalFsBlobStore(Files.createTempDirectory("treehub-obj"))
93+
94+
val objectStore = new ObjectStore(localFsBlobStore)
9395

9496
val deltaStore = new LocalDeltaStorage(Files.createTempDirectory("treehub-deltas"))
9597

0 commit comments

Comments
 (0)